Data Assessment
Intellectual Property (Copyrigths)
I would like to acknowledge that every single content exposed in this repository is based on protected songs. All the rigths of the data used come from the Spotify API, Genius API, but mainly the sonwriters and composers of the songs. In no way it is intented to make it as my own. All the mistakes made are my own and no one else.
Songs selection: Spotify API
I first asing all the packages needed into the vector packages. In this section the important package to query the Spotify API is spotifyr.
packages <- c('spotifyr','lubridate','ggplot2','dplyr','tidytext','stringr','tidyr','viridis','wordcloud', "tm",'forcats','fmsb','scales','radarchart','qdap','knitr','geniusr','tidyverse','plotly')
lapply(packages,require,character.only=T)
rm(packages)
The package requieres a Client ID and a Client Secret to query the API. To do so, the user must have a premium account in order to create a developers account. The proccess could be done here. Once this process is done, you can pull spotify access token into R with get_spotify_access_token(). Note that you could pass your ID and secret in order to set your credentials into System Environment. For more information on the packages reference to Charlie Thompson’s spotifyr Github repository.
Sys.setenv(SPOTIFY_CLIENT_ID = 'xxxxxxxxxxxxxxxxxxxxx')
Sys.setenv(SPOTIFY_CLIENT_SECRET = 'xxxxxxxxxxxxxxxxxxxxx')
access_token <- get_spotify_access_token()
To get Pearl Jam’s songs audio features we use the function get_artist_audio_features(). This functions queries the Spotify API and returns information on several characteristics such as: danceability, energy, instrumentalness, valene, explicit content, track name, track album, etc. Now, I’ll only keep songs featuring in the 11 albums. The reason is to eliminate live performances and covers and focus on self-written songs. Lastly we verify there are no repeated sing in the data set.
albums <- c("Riot Act", "Ten", "Yield", "Gigaton", "Backspacer", "Vs.", "Pearl Jam", "No Code", "Vitalogy", "Binaural", "Lightning Bolt")
pj <- pj[pj$album_name %in% albums,]
pj <- mutate(pj, dupli=ifelse(duplicated(pj$track_name)==T,1,0))
pj <- subset(pj,dupli == 0)
head(pj,5) %>% select(track_name, album_name,artist_name,album_release_date,danceability,instrumentalness,energy,valence, key_name,mode_name) %>% kable(format = "simple", col.names = str_to_title(gsub("[_]", " ", colnames(.))),align = 'lccccccccc',caption = "An example table caption.",digits = 3)
An example table caption.
| retrograde |
Gigaton |
Pearl Jam |
2020-03-27 |
0.457 |
0.000 |
0.616 |
0.277 |
G# |
major |
| johnny guitar |
Backspacer |
Pearl Jam |
2009-09-19 |
0.549 |
0.000 |
0.921 |
0.825 |
D |
major |
| just breathe |
Backspacer |
Pearl Jam |
2009-09-19 |
0.473 |
0.000 |
0.348 |
0.283 |
C |
major |
| immortality - remastered |
Vitalogy |
Pearl Jam |
1994-11-22 |
0.633 |
0.007 |
0.543 |
0.118 |
D |
major |
| daughter (remastered) |
Vs. |
Pearl Jam |
1993-10-19 |
0.615 |
0.004 |
0.702 |
0.734 |
G |
major |
Lyrics: Genius API
As for Spotify’s API, the Genius API requieres a developers account in order to query information. To authenticate the information the user must: 1. Create a Genius API client here, 2. generate a client access token form the API Clients Page and 3. set your credential in the System environment variable GENIUS_API_TOKE calling the function genius_token(). Now, in order to fecth the lyrics for each song I created a loop that goes trhough every song in the data set and retrieves the lyrics in a single vector and then adds it to the create variable lyrics2.
pj <- mutate(pj, lyrics2 = "")
for (element in (1:nrow(pj))) {
# I created the loop with two cicles in it in order to double check all the songs
# get their corresponding lyrics. Note that the loop isolates each song and
# retrieves the information with the function et_lyrics_search()
title <- str_to_title(pj$track_name[element])
print(title)
lyrics <- get_lyrics_search(artist_name = "Pearl Jam",
song_title = title)
if (nrow(lyrics)!=0) {
# I optedto reduce the dimensions of the retrieved data set to one observation.
# Now I save the first line and add the rest of the lines of the song
lyrics2 <- ""
for (piece in (1:nrow(lyrics))) {
if (piece ==1) {
lyrics2 <- lyrics$line[piece]
}
else{
lyrics2 <- paste(lyrics2, lyrics$line[piece], collapse = " ")
}
print(title)
print(lyrics2)
}
}
# If any song had any problem with querying the lyrics the second part of the loop
# repeats the procces to guarantee that all songs are assinged to their lyrics.
if (nrow(lyrics)==0) {
lyrics <- get_lyrics_search(artist_name = "Pearl Jam",
song_title = title)
lyrics2 <- ""
for (piece in (1:nrow(lyrics))) {
if (piece ==1) {
lyrics2 <- lyrics$line[piece]
}
else{
lyrics2 <- paste(lyrics2, lyrics$line[piece], collapse = " ")
}
print(title)
print(lyrics2)
}
}
# Finally the song is added to the original data
pj$lyrics2[element] <- lyrics2
}
Our data set now contains lyrics to all the 146 songs in the 11 albums. A glimpse to my top two Pearl Jam songs (according to spotify rankings) Black and Even flow:
songs <- c('Black', 'Even Flow')
pj[pj$track_name %in% songs,] %>% select(artist_name, album_name, track_name, lyrics) %>%
kable(format = "simple",col.names = str_to_title(gsub("[_]", " ", colnames(.))),align = 'lccccccccc')
| 18 |
Pearl Jam |
Ten |
Even Flow |
freezin’ rests his head on a pillow made of concrete again oh feelin’ maybe he will see a little better set of days ooh yeah oh hand out faces that he sees time again ain’t that familiar oh yeah oh dark grin he can’t help when he’s happy he looks insane hm yeah even flow thoughts arrive like butterflies oh he don’t know so he chases them away oh someday yet he will begin his life again life again life again kneelin’ looking through the paper though he doesn’t know to read oh yeah oh prayin’ now to something that has never showed him anything oh feelin’ understands the weather of the winters on its way oh ceilings few and far between all the legal halls of shame hey even flow thoughts arrive like butterflies oh he don’t know so he chases them away oh someday yet he will begin his life again ah whispering hands gently lead him away him away him away yeah woo ah yeah fuck it up interlude hey man you got a dollar come on just some spare change man i know you got well god bless you man god bless you even flow thoughts arrive like butterflies oh he don’t know so he chases them away oh someday yet he will begin his life again oh whispering hands gently lead him away him away him away yeah woo ah yeah |
| 19 |
Pearl Jam |
Ten |
Black |
hey hey yeah uh sheets of empty canvas untouched sheets of clay were laid spread out before me as her body once did all five horizons revolved around her soul as the earth to the sun now the air i tasted and breathed has taken a turn mm and all i taught her was everything mm i know she gave me all that she wore and now my bitter hands chafe beneath the clouds of what was everything oh the pictures have all been washed in black tattooed everything i take a walk outside i’m surrounded by some kids at play i can feel their laughter so why do i sear mm and twisted thoughts that spin ’round my head i’m spinning oh i’m spinning how quick the sun can drop away and now my bitter hands cradle broken glass of what was everything all the pictures have all been washed in black tattooed everything all the love gone bad turned my world to black tattooed all i see all that i am all i’ll be yeah mm mhm oooh i know someday you’ll have a beautiful life i know you’ll be a star in somebody else’s sky but why why why can’t it be oh can’t it be mine ooh ah yeah ah ooh doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo doodoo-doo-doo-doodoodoo ooh ooh ooh doodoo-doo-doo-doodoodoo ooh |
Finally, we have to take into account instumental songs. Even though Pearl Jam doesn’t have many instrumental songs 3 cases need to be adressed. these are Arc, Aya Davanita and Cready Stomp. I proced then to assing a missing value to these songs in order to implement the followong analysis.
songs <- c("Arc","Aya Davanita - Remastered","Cready Stomp - Bonus Track")
pj[pj$track_name %in% songs,grep('lyrics', colnames(pj))]
## [1] "awoohaye davanitaawoohawoohajahoohjahooh repeat awoohaye davanitaawoohawooha x"
## [2] "vocal"
## [3] "intrumental"
pj[pj$track_name %in% songs,grep('lyrics', colnames(pj))] <- NA
Data exploration
Tokens & data wrangling
The dataset is almost ready for use. I now execute some few data wrangling steps in order to have all the information needed. I use the package tm in order to remove punctuation, stop words and stemming. I begin by compiling all lyrics and create a corpus using the function VectorSource to read it a s a document and then pass the documents through the Corpus to create the final corpus. Note that all the document transformation of the document are done through the funciton tm_map, applying a function to all elements of the document. The process I follow is: 1) transform all upper-case letter to lower-case; 2) remove all punctionation; 3) remove English stop words; and 4) stem all the white spaces.
lyrics <- Corpus(VectorSource(pj$lyrics))
lyrics <- tm_map(lyrics, content_transformer(tolower))
lyrics <- tm_map(lyrics, removePunctuation)
lyrics <- tm_map(lyrics, removeWords, stopwords("english"))
lyrics <- tm_map(lyrics, stripWhitespace)
lyrics <- lyrics %>% unlist()
pj$lyrics <- as.character(lyrics[1:nrow(pj)])
pj$lyrics <- str_trim(pj$lyrics)
After procesing the lyrics there’s still some special cases that need to be taken into account ir order to prevent bias in the results. First, word contractions need to be addressed. As I removed all punctuation before, some contractions such as she’s will be now shes and are not taken as stop words by the function tm_map. Other cases are rythmitic vocals. Take Black as an example. The last minute of the song Eddie goes on like doo doo doo doo doo doo doo. In order to prevent biased results I ommit all rythmitic vocals and replace the contractions by their respective joint words.
pj$lyrics <- str_replace_all(pj$lyrics, 'bottomoh', ' bottom oh ')
pj$lyrics <- str_replace_all(pj$lyrics, ' heyim ', ' hey I am ')
pj$lyrics <- str_replace_all(pj$lyrics, ' deepoh ', ' deep oh ')
pj$lyrics <- str_replace_all(pj$lyrics, ' butterfliesdont ', ' butterflies do not ')
pj$lyrics <- str_replace_all(pj$lyrics, ' awaysomeday ', ' away someday ')
pj$lyrics <- str_replace_all(pj$lyrics, ' risingnext ', ' rising next ')
pj$lyrics <- str_replace_all(pj$lyrics, ' uhohoh ', ' oh ')
pj$lyrics <- str_replace_all(pj$lyrics, ' angellest ', ' angel lets ')
pj$lyrics <- str_replace_all(pj$lyrics, ' doodoodoodoodoodoodoo ', '')
pj$lyrics <- str_replace_all(pj$lyrics, ' yeah ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' yeah', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' ooh ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' oh ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' ah ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' hm ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' ye ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' ay ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' yeh ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' ya ', 'you')
pj$lyrics <- str_replace_all(pj$lyrics, ' mm ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' interlude ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' woo ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' mhm ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' oooh ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' ooooh ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' hey ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' uh ', ' ')
pj$lyrics <- str_replace_all(pj$lyrics, ' youll ', ' you will ')
pj$lyrics <- str_replace_all(pj$lyrics, ' cant ', ' can not ')
pj$lyrics <- str_replace_all(pj$lyrics, 'cant ', ' can not ')
pj$lyrics <- str_replace_all(pj$lyrics, ' im ', ' I am ')
pj$lyrics <- str_replace_all(pj$lyrics, ' ill ', ' I will ')
pj$lyrics <- str_replace_all(pj$lyrics, ' didnt ', ' did not ')
pj$lyrics <- str_replace_all(pj$lyrics, ' dont ', ' do not ')
pj$lyrics <- str_replace_all(pj$lyrics, ' hes ', ' he is ')
pj$lyrics <- str_replace_all(pj$lyrics, ' uhhuh ', '')
pj$lyrics <- str_replace_all(pj$lyrics, ' its' , '')
pj$lyrics <- str_replace_all(pj$lyrics, ' id ', ' I would ')
pj$lyrics <- str_replace_all(pj$lyrics, ' youre ', ' you are ')
pj$lyrics <- str_replace_all(pj$lyrics, ' youve ', ' you have ')
pj$lyrics <- str_replace_all(pj$lyrics, ' youd ', ' you would ')
pj$lyrics <- str_replace_all(pj$lyrics, ' theres ', ' there is ')
pj$lyrics <- str_replace_all(pj$lyrics, ' theyll ', ' they will ')
pj$lyrics <- str_replace_all(pj$lyrics, ' whats ', ' what is ')
pj$lyrics <- str_replace_all(pj$lyrics, ' doesnt ', ' does not ')
pj$lyrics <- str_replace_all(pj$lyrics, ' ive ', ' I have ')
pj$lyrics <- str_replace_all(pj$lyrics, ' hadnt ', ' had not ')
pj$lyrics <- str_replace_all(pj$lyrics, ' wouldnt ', ' would not ')
pj$lyrics <- str_replace_all(pj$lyrics, ' til ',' until ')
pj$lyrics <- str_replace_all(pj$lyrics, ' wouldve ', ' would have ')
pj$lyrics <- str_replace_all(pj$lyrics, ' shes ', ' she is ')
pj$lyrics <- str_replace_all(pj$lyrics, ' thats ', ' that is ')
One last step into our final dataset is word sentiment. I then turn the data into a tidy format using the package tidytext. I use the function unnest_tokens in order to split the lyrics into indivudual words. the function get_sentiments is usedto obtain the sentiments lexicon in a tidy format. The lexicon used is NCR Word-Emotion Association Lexicon. This lexicon lists English words and matches their associations with eight basic emotions (anger, fear, anticipation, trust, surprise, sadness, joy, and disgust) and two sentiments (negative and positive). Further information could be found in the official web page. With the sentiments matched I’m able to count the number of words per emotion and sentiment.
pearl_songs <- pj %>% unnest_tokens(word, lyrics) # Separate the lyrics into single words
cleaned_pearl <- pearl_songs %>% anti_join(stop_words) # Eliminating stop words
nrc <- get_sentiments(lexicon = "nrc") # Categorical words sentiments
pearl_sentiment <- cleaned_pearl %>%
inner_join(nrc,by = c('word')) %>% # Merging the word sentiment data with the words dataset
count(track_name, sentiment) %>% # Counting the number of words per sentiment
spread(sentiment, n, fill = 0) # Reshaping the data from observations into columns
columns <- c(grep('album_name', colnames(pj)),
grep('track_name', colnames(pj)))
pearl_sentiment <- inner_join(x = pearl_sentiment, y = pj[,columns], by = "track_name")
pj <- inner_join(pj, pearl_sentiment)
Gloom Index
With the final dataset I’m able to do some sort of analysis to Pearl Jam’s discography. At first hand I was interested in finding some metric of sadness in a musical context. One of the most interesting analysis taking this into account is the Gloom Index developed by Charlie Thompson. Here the author uses the information on the valence of the song, the percentage of sad songs and the lyrical density of the song. In terms of valence, Spotify uses a wide variety of inputs to determine weather a songs sound happy or not. With this in mind sad songs have a lower valence score and happy songs have high valence. Here’s a preview of some of Pearl Jam’s saddest songs according this measure:
pj %>% group_by(track_name) %>% summarise(valence = mean(valence)) %>% arrange(valence) %>% head(10) %>% kable(format = "simple", col.names = str_to_title(gsub("[_]", " ", colnames(.))),align = 'lc',caption = "Top ten saddest Pearl Jam songs - Valence score",digits = 3)
## `summarise()` ungrouping output (override with `.groups` argument)
Top ten saddest Pearl Jam songs - Valence score
| Nothing As It Seems |
0.044 |
| Of The Girl |
0.049 |
| Sometimes |
0.062 |
| Release |
0.064 |
| All Those Yesterdays |
0.068 |
| Inside Job |
0.074 |
| I’m Open |
0.098 |
| Wishlist |
0.101 |
| Crazy Mary (Remastered) |
0.104 |
| Wasted Reprise |
0.104 |
Originally the index takes the following form:
\[Gloom\;Index = \frac{(1-valence) + percentage\_sad(1+lyrical\_density)}{2}\] Now, noting that the lyrical density migth be misleading I turn to other measure of lyrical density. Longer songs might have more repeated lyrics than shorter songs. This can be either by more choruses or bridges, but the results might be wrong for this reason. Accounting for this problem a cleaner metric for lyrical density is the ratio of disticnt words and total words used. \[Glomm\; Index = \frac{(1-valence)+percentage\_sad(1+distinct\_percentage)}{2}\]
Where
\[distinct\_percentage = \frac{\# distinct \;words}{\# total words}\]
Now, having this information how can this index be interpreted? We need to understand how gloom moves along the different inputs. First, valence shows higher values for happy songs whilst low values for sad songs. Now, by subtracting the valence, a song with lower valence will have a higher index score. In other words, valence has an inverse relationship with the gloom index. Now, turbulent words and distinct words percentage logically move in the same direction of the gloom index. I can now say that higher values show sadder songs, while lower values represent happier songs. Note that compared to Charlie’s Radiohead analysis my index is interpreted in a completely different way. The main reason is because Charlie rescales his index in order to represent happy songs with higher values. Now, the interpretation of the results are analogous, the only difference are the scale in which they are presented.
pj <- pj %>% unnest_tokens(word, lyrics) %>%
group_by(track_name) %>% summarise(distinct_words = n_distinct(word)) %>%
inner_join(pj)
## `summarise()` ungrouping output (override with `.groups` argument)
## Joining, by = "track_name"
pj <- mutate(pj, words = sapply(strsplit(pj$lyrics, " "), length))
pj <- mutate(pj, seconds = duration_ms/1000)
pj <- mutate(pj, words_per_second = words/seconds)
pj <- mutate(pj, sad_percentage = sadness/distinct_words)
pj <- mutate(pj, dist_per = (distinct_words/words))
pj <- mutate(pj, index = ((1-valence)+sad_percentage*(1+dist_per))/2)
I can now vizualize different information I have gathered along the document. First, we can see the saddest songs according to the Gloom Index.
pj %>% group_by(track_name,album_name) %>%
summarise(index = mean(index),
valence = mean(valence),
`turbulent words` = mean(sad_percentage),
`distinct percentage words` = mean(dist_per)) %>% arrange(desc(index)) %>% head(10) %>% kable(format = "simple", col.names = str_to_title(gsub("[_]", " ", colnames(.))),align = 'lccccc',caption = "Top ten saddest Pearl Jam songs - Gloom Index",digits = 3)
## `summarise()` regrouping output by 'track_name' (override with `.groups` argument)
Top ten saddest Pearl Jam songs - Gloom Index
| Spin The Black Circle - Remastered |
Vitalogy |
0.774 |
0.141 |
0.525 |
0.312 |
| Comatose |
Pearl Jam |
0.754 |
0.221 |
0.500 |
0.460 |
| Red Bar |
Yield |
0.733 |
0.700 |
0.667 |
0.750 |
| Deep |
Ten |
0.606 |
0.250 |
0.326 |
0.422 |
| Inside Job |
Pearl Jam |
0.543 |
0.074 |
0.097 |
0.653 |
| Of The Girl |
Binaural |
0.527 |
0.049 |
0.061 |
0.717 |
| Love Boat Captain |
Riot Act |
0.522 |
0.176 |
0.137 |
0.605 |
| Once |
Ten |
0.518 |
0.211 |
0.151 |
0.631 |
| Release |
Ten |
0.514 |
0.064 |
0.057 |
0.603 |
| Sometimes |
No Code |
0.514 |
0.062 |
0.050 |
0.784 |
We have now the most depressing Pearl Jam song to date: Spin the Black Circle. But how can it be? Anyone who have heard the rigth ammount of their songs could argue in favor of Release or All those yesterdays, or even Black, but remember that this is a data driven measure. Spin the Black Circle actually has a rather low valence score (meaning is a sad song), ranking 19th with 0.141. But the percentage of sad words is the second highest of all songs, behind Red Bar (which repeats seven times) wih a percentage of sad word of 52%. If the ranking seems odd listen to the songs and see it for yourself.
To see how sadness or gloom has evolved across time, is posible to average the index per album and see the dynamic. This is the ranking for the saddest albums:
pj %>% group_by(album_name) %>%
summarise(index = mean(index),
valence = mean(valence),
`sad words` = mean(sad_percentage),
`distinct percentage words` = mean(dist_per)) %>% arrange(desc(index)) %>% head(12) %>% kable(format = "simple", col.names = str_to_title(gsub("[_]", " ", colnames(.))),align = 'lccccc',caption = "Saddest Pearl Jam albums - Gloom Index",digits = 3)
## `summarise()` ungrouping output (override with `.groups` argument)
Saddest Pearl Jam albums - Gloom Index
| Ten |
0.432 |
0.244 |
0.071 |
0.561 |
| Yield |
0.399 |
0.332 |
0.075 |
0.642 |
| Vitalogy |
0.394 |
0.311 |
0.068 |
0.545 |
| Pearl Jam |
0.379 |
0.408 |
0.105 |
0.596 |
| No Code |
0.343 |
0.360 |
0.028 |
0.632 |
| Riot Act |
0.336 |
0.430 |
0.064 |
0.644 |
| Gigaton |
0.325 |
0.484 |
0.082 |
0.625 |
| Vs. |
0.309 |
0.445 |
0.044 |
0.477 |
| Binaural |
0.293 |
0.489 |
0.045 |
0.704 |
| Lightning Bolt |
0.272 |
0.577 |
0.072 |
0.668 |
| Backspacer |
0.251 |
0.604 |
0.069 |
0.546 |
Now, it is posible to include both results from the last two tables in one graph in order to compare the results. Each point in the graph represents a songs, and for each song theres a gloom index score. The black dots represent the average score for each album. It is improtant to note that by the tim pearl jam songs seem to be growing less sad.
pal <- c("turquoise4","navyblue","peru","goldenrod2","forestgreen","darkgray",
"orange","magenta","lightslateblue","yellowgreen","mediumblue")
fig <- plot_ly(data = rank,
x = ~album_name, y = ~average,type = 'scatter',mode = 'markers',
marker = list(size = 10),
color = ~album_name, colors = pal, span = I(1),stroke = I("black"),
text = ~paste("Gloom Index score: ", round(average,3), '<br>Track:', track_name))
fig <- fig %>% add_trace(y = ~gloom_index,mode = 'lines+markers',type = 'scatter',name = 'Album Average',
color = I('black'),stroke = I("black"),marker = list(size = 10),
text = ~paste("Album average valence:", round(gloom_index,3), '<br>Album:',album_name))
fig %>% layout(xaxis = list(zeroline = T, title ="Album Name"),
yaxis = list(hoverformat = '.3f', title = 'Gloom Index'),
showlegend = F)
## Warning: `arrange_()` is deprecated as of dplyr 0.7.0.
## Please use `arrange()` instead.
## See vignette('programming') for more help
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.
With this graph we can conclude some things. First, the saddest Pearl Jam album to date is
Ten, followed by
Yield and
Vitalogy. On the other hand, the happiest albums being Backspacer and Lightning Bolt and the tops sad songs being
Parting ways and
Supersonic.
Word counts by album and song
With the dataset I’m able to do some extra exploration. For example, how many words does each album have in average? How many words per second? What’s the wordiest song and album? In order to answer this question is important to rely in an important word count statistic. For this matter i will use the count of distictive words used in the song. But, why this statistic? I considered distinct words in order to avoid reptetiveness. For example, in analizing Deep, you can hear about 7 times can’t touch the bottom. In doing so we are not being guided by the repetiveness of the song, but the different words used.
pj %>% group_by(track_name,album_name) %>% summarise(distinct_words = mean(distinct_words),
word_per_song = mean(words),
words_per_second = mean(words_per_second)) %>% arrange(desc(distinct_words)) %>% head(10) %>%
kable(format = "simple", col.names = str_to_title(gsub("[_]", " ", colnames(.))),align = 'lcccc',caption = "Pearl Jam word count by song",digits = 3)
## `summarise()` regrouping output by 'track_name' (override with `.groups` argument)
Pearl Jam word count by song
| Seven O’clock |
Gigaton |
154 |
187 |
0.500 |
| Who Ever Said |
Gigaton |
131 |
187 |
0.600 |
| Stupidmop - Remastered |
Vitalogy |
128 |
444 |
1.000 |
| My Father’s Son |
Lightning Bolt |
119 |
176 |
0.951 |
| River Cross |
Gigaton |
105 |
161 |
0.454 |
| Comes Then Goes |
Gigaton |
104 |
142 |
0.392 |
| Never Destination |
Gigaton |
104 |
187 |
0.726 |
| Satan’s Bed - Remastered |
Vitalogy |
103 |
173 |
0.844 |
| Lightning Bolt |
Lightning Bolt |
97 |
119 |
0.470 |
| Crazy Mary (Remastered) |
Vs. |
96 |
155 |
0.457 |
Wordclouds
At this point I’am able to use the filtered data into wordclouds by using the word counts for each song. By doing so the main objective is to vizualize the pricipal words used in each song by adding the total words used in each song across albums. I use the packages wordcloud and tm. I then follow to use the functions wordcloud(from wordcloud) and DocumentTermMatrix, Corpus and VectorSource (from tm). I plot the top 100 words selected by the word counts in each album (aggreated by the sum of each words count by song)
A small caveat in this section: Word counts do not account for song repetiiveness. So word usage might be biased in terms of repetition within each song and album. Section 2.6 (Term frequency Inverse Document Frequency (TF-IDF)) takes this problem into account by using a measure of the importance of each word in teach song.
Ten 
Vs. 
Vitalogy 
No Code 
Yield 
Binaural 
Riot Act 
Pearl Jam 
Backspacer 
Lighthing Bolt 
Gigaton 
Vocabulary diversity
pj %>% group_by(album_name,track_name) %>% summarise(number_words = mean(distinct_words)) %>%
ggplot(data = ., aes(x = factor(album_name),y = number_words)) +geom_violin(alpha = 0.7, color = 'black', size = 0.2)+
geom_jitter(aes(size = number_words),height = 0, width = 0.1, alpha =0.2) + geom_boxplot(aes(fill = album_name), alpha = 0.6) +
labs(title = "Lexical Diversity - Vocabulary", x = "", y = 'Distinct words count') + theme_bw() +
theme(plot.title = element_text(hjust = 0.5, size = 10),
axis.title = element_text(size = 8),
axis.text = element_text(size = 7),
legend.title = element_text(size = 8),
legend.position = "bottom",
panel.grid.major = element_line(size = 0.4)) + guides(fill = F) + scale_size('Number of words')
## `summarise()` regrouping output by 'album_name' (override with `.groups` argument)

Term Frequency Inverse Document Frequency (TF-IDF)
album_levels1 <- c("Ten", "Vs.", "Vitalogy", "No Code", "Yield", "Binaural")
album_levels2 <- c("Riot Act", "Pearl Jam", "Backspacer", "Lightning Bolt", "Gigaton")
frequency[frequency$album_name %in% album_levels1,] %>% group_by(album_name) %>%
slice_max(tf_idf, n = 15) %>% ungroup() %>%
ggplot(aes(tf_idf,fct_reorder(word, tf_idf), fill = album_name)) +
geom_col(show.legend = F) + facet_wrap(~album_name, ncol = 3, scales = "free") +
labs(x = "tf-idf", y = NULL)+theme(axis.text = element_text(size = 7.5))

frequency[frequency$album_name %in% album_levels2,] %>% group_by(album_name) %>%
slice_max(tf_idf, n = 15) %>% ungroup() %>%
ggplot(aes(tf_idf,fct_reorder(word, tf_idf), fill = album_name)) +
geom_col(show.legend = F) + facet_wrap(~album_name, ncol = 3, scales = "free") +
labs(x = "tf-idf", y = NULL)+theme(axis.text = element_text(size = 7.5))

Sentiment Analysis and Natural Language Processing
NCR Sentiment
pj$album_name <- factor(pj$album_name, levels = album_levels)
pj[pj$album_name %in% album_levels1,] %>% group_by(album_name) %>% summarise(fear = sum(fear),
anger = sum(anger),
anticipation = sum(anticipation),
disgust = sum(disgust),
joy = sum(joy),
# negative = sum(negative),
# positive = sum(positive),
sadness = sum(sadness),
surprise = sum(surprise),
trust = sum(trust)) %>%
reshape2::melt(id.vars = c('album_name'), value.name = 'sentiment') %>%
ggplot(aes(x = variable, y = sentiment, fill = sentiment)) + geom_bar(stat = 'identity') + coord_flip()+
facet_wrap(~album_name, ncol = 3, scales = "free") + theme_bw() + scale_fill_viridis() +
labs(y = "Word count", x = "NCR Sentiment") + theme(legend.position = 'none')
## `summarise()` ungrouping output (override with `.groups` argument)

pj[pj$album_name %in% album_levels2,] %>% group_by(album_name) %>% summarise(fear = sum(fear),
anger = sum(anger),
anticipation = sum(anticipation),
disgust = sum(disgust),
joy = sum(joy),
# negative = sum(negative),
# positive = sum(positive),
sadness = sum(sadness),
surprise = sum(surprise),
trust = sum(trust)) %>%
reshape2::melt(id.vars = c('album_name'), value.name = 'sentiment') %>%
ggplot(aes(x = variable, y = sentiment, fill = sentiment)) + geom_bar(stat = 'identity') + coord_flip()+
facet_wrap(~album_name, ncol = 3, scales = "free") + theme_bw() + scale_fill_viridis() +
labs(y = "Word count", x = "NCR Sentiment") + theme(legend.position = 'none')
## `summarise()` ungrouping output (override with `.groups` argument)

Radarplots
radar <- pj[,c(grep('album_name', colnames(pj)),
grep('fear', colnames(pj)),
grep('anger', colnames(pj)),
grep('anticipation', colnames(pj)),
grep('disgust', colnames(pj)),
grep('sadness', colnames(pj)))] %>%
group_by(album_name) %>% summarise(fear = mean(fear),
anger = mean(anger),
anticipation = mean(anticipation),
disgust = mean(disgust),
sadness = mean(sadness))
## `summarise()` ungrouping output (override with `.groups` argument)
colnames(radar) <- str_to_title(colnames(radar))
chartJSRadar(radar)
radar <- pj[,c(grep('album_name', colnames(pj)),
grep('joy', colnames(pj)),
grep('surprise', colnames(pj)),
grep('trust', colnames(pj)))] %>%
group_by(album_name) %>% summarise(joy = mean(joy),
surprise = mean(surprise),
trust = mean(trust)
)
## `summarise()` ungrouping output (override with `.groups` argument)
colnames(radar) <- str_to_title(colnames(radar))
chartJSRadar(radar)
radar <- pj[,c(grep('album_name', colnames(pj)),
grep('positive', colnames(pj)),
grep('negative', colnames(pj)))] %>%
group_by(album_name) %>% summarise(positive = mean(positive),
negative = mean(negative)
)
## `summarise()` ungrouping output (override with `.groups` argument)
colnames(radar) <- str_to_title(colnames(radar))
chartJSRadar(radar)
Bi-grams and tri-grams
bigrams <- pj[,c(grep('track_name', colnames(pj)),
grep('album_name', colnames(pj)),
grep('lyrics', colnames(pj)))] %>%
unnest_tokens(bigram, lyrics, token = "ngrams", n = 2) %>%
separate(bigram, c("word1", "word2"), sep = " ") %>%
filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word) %>%
filter(word1 != word2) %>%
unite(bigram, word1, word2, sep = " ") %>%
inner_join(pj) %>%
count(bigram, album_name, sort = TRUE) %>%
group_by(album_name) %>%
slice(seq_len(10)) %>%
ungroup() %>% arrange(album_name,n)
## Joining, by = c("track_name", "album_name")
bigrams$album_name <- factor(bigrams$album_name, levels = album_levels)
ggplot(data = bigrams[bigrams$album_name %in% album_levels1,], aes(x = bigram, y = n, fill = album_name))+
geom_col(show.legend = F) + coord_flip() + theme_bw()+ labs(y = 'Number of repetitions', x ='Bigram')+
facet_wrap(~album_name, scales = "free_y")

ggplot(data = bigrams[bigrams$album_name %in% album_levels2,], aes(x = bigram, y = n, fill = album_name))+
geom_col(show.legend = F) + coord_flip() + theme_bw()+ labs(y = 'Number of repetitions', x ='Bigram')+
facet_wrap(~album_name, scales = "free_y")

LS0tCmF1dGhvcjogIkpvcmdlIEx1aXMgT2Nob2EiCnRpdGxlOiAiUGVhcmwgSmFtIEFuYWx5c2lzIGluIFIiCmRhdGU6ICIxLzEyLzIwMjEiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgaGlnaGxpZ2h0OiB0ZXh0bWF0ZQogICAgIyB0aGVtZTogY29zbW8KICAgIHRoZW1lOiBsdW1lbgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCnJpZ2h0OgogIC0gdGV4dDogR2l0SHViCiAgICBocmVmOiBodHRwczovL2dpdGh1Yi5jb20KLS0tCgpgYGB7ciAsIGluY2x1ZGU9Rn0KIyBwaiA8LSByZWFkUkRTKGZpbGUgPSAnRGF0YS9wZWFybC5SRFMnKQpwaiA8LSByZWFkUkRTKGZpbGUgPSAnRGF0YS9wZWFybCBqYW0gMi5SRFMnKQpyb3dzIDwtIHNhbXBsZShucm93KHBqKSkKcGogPC0gcGpbcm93cyxdCmBgYAoKIyMgTXVzaWNhbCBhbmQgbHlyaWNhbCBhbmFseXNpcyBvZiBQZWFybCBKYW0ncyBzb25ncwoKVGhpcyByZXBvc2l0b3J5IGNvbXByaXNlcyBhIG11c2ljYWwgYW5kIGx5cmljYWwgYW5hbHlzaXMgb2YgUGVhcmwgSmFtJ3Mgc29uZ3MuIEkgdXNlIGRhdGEgZnJvbSBTcG90aWZ5J3MgYW5kIEdlbml1cydzIEFQSXMuIEkgdXNlIHNjcmlwdHMgdG8gcXVlcnkgaW5mb3JtYXRpb24gb24gdGhlIG11c2ljYWwgcHJvcGV0eSBvZiBhbGwgb2YgdGhlIDExIFBlYXJsIEphbSBhbGJ1bXMuIEkgbGF0ZXIgdXNlIGEgc2NyaXB0IHRvIHNjcmFwZSB0aGUgbHlyeWNzIHRvIHNldmVyYWwgc29uZ3MgYW5kIGZpbmFsbHkgdXNlIHRoZSBHZW5pdXMgQVBJIHRvIGRvd25sb2FkIHRoZSByZW1haW5pbmcuIFJlbGF0ZWQgd29ya3MgaW5jbHVkZSBbS2VuZHJpY2sgTGFtYXIgc2VudGltZW50IGFuYWx5c2lzXShodHRwczovL2dpdGh1Yi5jb20vZGF2aWRrbGFpbmcva2VuZHJpY2svYmxvYi9tYXN0ZXIvUkVBRE1FLm1kKSwgW0dsb29tIEluZGV4XShodHRwczovL3d3dy5yY2hhcmxpZS5jb20vYmxvZy9maXR0ZXItaGFwcGllci8pIHRvIGZpbmQgUmFkaW9oZWFkJ3MgbW9zdCBkZXByZXNzaW5nIHNvbmcgYnkgW0NoYXJsaWUgVGhvbXBzb25dKGh0dHBzOi8vd3d3LnJjaGFybGllLmNvbSksIFtCb2IgRHlsYW4gbHlyaWNhbCBhbmFseXNpc10oaHR0cHM6Ly9ycHVicy5jb20vcGF1bF9yZWluZXJzLzQwNjM1OSkgYnkgUGF1bCBSZWluZXJzLCBbdGlkeSBzZW50aW1lbnQgYW5hbHlzaXNdKGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9jb21tdW5pdHkvdHV0b3JpYWxzL3NlbnRpbWVudC1hbmFseXNpcy1SKSBvbiBQcmluY2UncyBtdXNpYyBieSBEZWJiaWUgTGlza2UgYW5kIFtNdXNpY2FsIEx5cmljcyBBbmFseXNpc10oaHR0cHM6Ly9ycHVicy5jb20vQnJlZU1jTGVubmFuL211c2ljX2x5cmljX2FuYWx5c2lzKSBvbiBzZXZlcmFsIGFydGlzdHMgYnkgQnJlZSBNY0xlbm5hbi4KClRoZSByZXBvIGlzIG9yZ2FuaXplZCBhcyBmb2xsb3dzOgoKCiogKipEYXRhIEFzc2Vzc21lbnQqKgogICsgKipJbnRlbGxlY3R1YWwgUHJvcGVydHkgKENvcHlyaWd0aHMpKioKICArICoqU29uZ3Mgc2VsZWN0aW9uOiBTcG90aWZ5IEFQSSoqCiAgKyAqKkx5cmljczogR2VuaXVzIEFQSSoqCgoqICoqRGF0YSBleHBsb3JhdGlvbioqCiAgKyAqKlRva2VucyAmIGRhdGEgd3JhbmdsaW5nKioKICArICoqR2xvb20gSW5kZXgqKgogICsgKipXb3JkIGNvdW50cyBieSBhbGJ1bSBhbmQgc29uZyoqCiAgKyAqKldvcmRjbG91ZHMqKgogICsgKipWb2NhYnVsYXJ5IGRpdmVyc2l0eSoqCiAgKyAqKlRlcm0gRnJlcXVlbmN5IEludmVyc2UgRG9jdW1lbnQgRnJlcXVlbmN5IChURi1JREYpKioKCiogKipTZW50aW1lbnQgQW5hbHlzaXMgYW5kIE5hdHVyYWwgTGFuZ3VhZ2UgUHJvY2Vzc2luZyoqCiAgKyAqKk5DUiBTZW50aW1lbnQqKgogICsgKipCaS1ncmFtcyBhbmQgdHJpLWdyYW1zKioKICAKIyBEYXRhIEFzc2Vzc21lbnQKIyMgSW50ZWxsZWN0dWFsIFByb3BlcnR5IChDb3B5cmlndGhzKQoKSSB3b3VsZCBsaWtlIHRvIGFja25vd2xlZGdlIHRoYXQgZXZlcnkgc2luZ2xlIGNvbnRlbnQgZXhwb3NlZCBpbiB0aGlzIHJlcG9zaXRvcnkgaXMgYmFzZWQgb24gcHJvdGVjdGVkIHNvbmdzLiBBbGwgdGhlIHJpZ3RocyBvZiB0aGUgZGF0YSB1c2VkIGNvbWUgZnJvbSB0aGUgU3BvdGlmeSBBUEksIEdlbml1cyBBUEksIGJ1dCBtYWlubHkgdGhlIHNvbndyaXRlcnMgYW5kIGNvbXBvc2VycyBvZiB0aGUgc29uZ3MuIEluIG5vIHdheSBpdCBpcyBpbnRlbnRlZCB0byBtYWtlIGl0IGFzIG15IG93bi4gQWxsIHRoZSBtaXN0YWtlcyBtYWRlIGFyZSBteSBvd24gYW5kIG5vIG9uZSBlbHNlLgoKIyMgU29uZ3Mgc2VsZWN0aW9uOiBTcG90aWZ5IEFQSQoKSSBmaXJzdCBhc2luZyBhbGwgdGhlIHBhY2thZ2VzIG5lZWRlZCBpbnRvIHRoZSB2ZWN0b3IgYHBhY2thZ2VzYC4gSW4gdGhpcyBzZWN0aW9uIHRoZSBpbXBvcnRhbnQgcGFja2FnZSB0byBxdWVyeSB0aGUgU3BvdGlmeSBBUEkgaXMgYHNwb3RpZnlyYC4KCmBgYHtyLCBldmFsPVQgLGluY2x1ZGU9Rn0KcGFja2FnZXMgPC0gYygnc3BvdGlmeXInLCdsdWJyaWRhdGUnLCdnZ3Bsb3QyJywnZHBseXInLCd0aWR5dGV4dCcsJ3N0cmluZ3InLCd0aWR5cicsJ3ZpcmlkaXMnLCd3b3JkY2xvdWQnLCAidG0iLCdmb3JjYXRzJywnZm1zYicsJ3NjYWxlcycsJ3JhZGFyY2hhcnQnLCdxZGFwJywna25pdHInLCdnZW5pdXNyJywndGlkeXZlcnNlJywncGxvdGx5JykKbGFwcGx5KHBhY2thZ2VzLHJlcXVpcmUsY2hhcmFjdGVyLm9ubHk9VCkKcm0ocGFja2FnZXMpCmBgYAoKYGBge3IsIGV2YWw9RiAsaW5jbHVkZT1UfQpwYWNrYWdlcyA8LSBjKCdzcG90aWZ5cicsJ2x1YnJpZGF0ZScsJ2dncGxvdDInLCdkcGx5cicsJ3RpZHl0ZXh0Jywnc3RyaW5ncicsJ3RpZHlyJywndmlyaWRpcycsJ3dvcmRjbG91ZCcsICJ0bSIsJ2ZvcmNhdHMnLCdmbXNiJywnc2NhbGVzJywncmFkYXJjaGFydCcsJ3FkYXAnLCdrbml0cicsJ2dlbml1c3InLCd0aWR5dmVyc2UnLCdwbG90bHknKQpsYXBwbHkocGFja2FnZXMscmVxdWlyZSxjaGFyYWN0ZXIub25seT1UKQpybShwYWNrYWdlcykKYGBgClRoZSBwYWNrYWdlIHJlcXVpZXJlcyBhIGBDbGllbnQgSURgIGFuZCBhIGBDbGllbnQgU2VjcmV0YCB0byBxdWVyeSB0aGUgQVBJLiBUbyBkbyBzbywgdGhlIHVzZXIgbXVzdCBoYXZlIGEgcHJlbWl1bSBhY2NvdW50IGluIG9yZGVyIHRvIGNyZWF0ZSBhIGRldmVsb3BlcnMgYWNjb3VudC4gVGhlIHByb2NjZXNzIGNvdWxkIGJlIGRvbmUgW2hlcmVdKGh0dHBzOi8vZGV2ZWxvcGVyLnNwb3RpZnkuY29tL2Rhc2hib2FyZC8pLiBPbmNlIHRoaXMgcHJvY2VzcyBpcyBkb25lLCB5b3UgY2FuIHB1bGwgc3BvdGlmeSBhY2Nlc3MgdG9rZW4gaW50byBSIHdpdGggYGdldF9zcG90aWZ5X2FjY2Vzc190b2tlbigpYC4gTm90ZSB0aGF0IHlvdSBjb3VsZCBwYXNzIHlvdXIgSUQgYW5kIHNlY3JldCBpbiBvcmRlciB0byBzZXQgeW91ciBjcmVkZW50aWFscyBpbnRvIFN5c3RlbSBFbnZpcm9ubWVudC4gRm9yIG1vcmUgaW5mb3JtYXRpb24gb24gdGhlIHBhY2thZ2VzIHJlZmVyZW5jZSB0byBDaGFybGllIFRob21wc29uJ3MgYHNwb3RpZnlyYCBHaXRodWIgW3JlcG9zaXRvcnldKGh0dHBzOi8vZ2l0aHViLmNvbS9jaGFybGllODYvc3BvdGlmeXIpLgoKYGBge3IsIGV2YWw9Rn0KU3lzLnNldGVudihTUE9USUZZX0NMSUVOVF9JRCA9ICd4eHh4eHh4eHh4eHh4eHh4eHh4eHgnKQpTeXMuc2V0ZW52KFNQT1RJRllfQ0xJRU5UX1NFQ1JFVCA9ICd4eHh4eHh4eHh4eHh4eHh4eHh4eHgnKQoKYWNjZXNzX3Rva2VuIDwtIGdldF9zcG90aWZ5X2FjY2Vzc190b2tlbigpCmBgYAoKVG8gZ2V0IFBlYXJsIEphbSdzIHNvbmdzIGF1ZGlvIGZlYXR1cmVzIHdlIHVzZSB0aGUgZnVuY3Rpb24gYGdldF9hcnRpc3RfYXVkaW9fZmVhdHVyZXMoKWAuIFRoaXMgZnVuY3Rpb25zIHF1ZXJpZXMgdGhlIFNwb3RpZnkgQVBJIGFuZCByZXR1cm5zIGluZm9ybWF0aW9uIG9uIHNldmVyYWwgY2hhcmFjdGVyaXN0aWNzIHN1Y2ggYXM6IGRhbmNlYWJpbGl0eSwgZW5lcmd5LCBpbnN0cnVtZW50YWxuZXNzLCB2YWxlbmUsIGV4cGxpY2l0IGNvbnRlbnQsIHRyYWNrIG5hbWUsIHRyYWNrIGFsYnVtLCBldGMuIE5vdywgSSdsbCBvbmx5IGtlZXAgc29uZ3MgZmVhdHVyaW5nIGluIHRoZSAxMSBhbGJ1bXMuIFRoZSByZWFzb24gaXMgdG8gZWxpbWluYXRlIGxpdmUgcGVyZm9ybWFuY2VzIGFuZCBjb3ZlcnMgYW5kIGZvY3VzIG9uIHNlbGYtd3JpdHRlbiBzb25ncy4gTGFzdGx5IHdlIHZlcmlmeSB0aGVyZSBhcmUgbm8gcmVwZWF0ZWQgc2luZyBpbiB0aGUgZGF0YSBzZXQuCgpgYGB7ciwgZXZhbD1UfQphbGJ1bXMgPC0gYygiUmlvdCBBY3QiLCAiVGVuIiwgIllpZWxkIiwgIkdpZ2F0b24iLCAiQmFja3NwYWNlciIsICJWcy4iLCAiUGVhcmwgSmFtIiwgIk5vIENvZGUiLCAiVml0YWxvZ3kiLCAiQmluYXVyYWwiLCAiTGlnaHRuaW5nIEJvbHQiKQpwaiA8LSBwaltwaiRhbGJ1bV9uYW1lICVpbiUgYWxidW1zLF0KcGogPC0gbXV0YXRlKHBqLCBkdXBsaT1pZmVsc2UoZHVwbGljYXRlZChwaiR0cmFja19uYW1lKT09VCwxLDApKQpwaiA8LSBzdWJzZXQocGosZHVwbGkgPT0gMCkKaGVhZChwaiw1KSAlPiUgc2VsZWN0KHRyYWNrX25hbWUsIGFsYnVtX25hbWUsYXJ0aXN0X25hbWUsYWxidW1fcmVsZWFzZV9kYXRlLGRhbmNlYWJpbGl0eSxpbnN0cnVtZW50YWxuZXNzLGVuZXJneSx2YWxlbmNlLCBrZXlfbmFtZSxtb2RlX25hbWUpICU+JSAga2FibGUoZm9ybWF0ID0gInNpbXBsZSIsIGNvbC5uYW1lcyA9IHN0cl90b190aXRsZShnc3ViKCJbX10iLCAiICIsIGNvbG5hbWVzKC4pKSksYWxpZ24gPSAnbGNjY2NjY2NjYycsY2FwdGlvbiA9ICJBbiBleGFtcGxlIHRhYmxlIGNhcHRpb24uIixkaWdpdHMgPSAzKQpgYGAKCiMjIEx5cmljczogR2VuaXVzIEFQSQoKQXMgZm9yIFNwb3RpZnkncyBBUEksIHRoZSBHZW5pdXMgQVBJIHJlcXVpZXJlcyBhIGRldmVsb3BlcnMgYWNjb3VudCBpbiBvcmRlciB0byBxdWVyeSBpbmZvcm1hdGlvbi4gVG8gYXV0aGVudGljYXRlIHRoZSBpbmZvcm1hdGlvbiB0aGUgdXNlciBtdXN0OiAxLiBDcmVhdGUgYSBgR2VuaXVzIEFQSSBjbGllbnRgIFtoZXJlXShodHRwczovL2dlbml1cy5jb20vYXBpLWNsaWVudHMvbmV3KSwgMi4gZ2VuZXJhdGUgYSBgY2xpZW50IGFjY2VzcyB0b2tlbmAgZm9ybSB0aGUgW0FQSSBDbGllbnRzIFBhZ2VdKGh0dHBzOi8vZ2VuaXVzLmNvbS9hcGktY2xpZW50cykgYW5kIDMuIHNldCB5b3VyIGNyZWRlbnRpYWwgaW4gdGhlIFN5c3RlbSBlbnZpcm9ubWVudCB2YXJpYWJsZSBgR0VOSVVTX0FQSV9UT0tFYCBjYWxsaW5nIHRoZSBmdW5jdGlvbiBgZ2VuaXVzX3Rva2VuKClgLiBOb3csIGluIG9yZGVyIHRvIGZlY3RoIHRoZSBseXJpY3MgZm9yIGVhY2ggc29uZyBJIGNyZWF0ZWQgYSBsb29wIHRoYXQgZ29lcyB0cmhvdWdoIGV2ZXJ5IHNvbmcgaW4gdGhlIGRhdGEgc2V0IGFuZCByZXRyaWV2ZXMgdGhlIGx5cmljcyBpbiBhIHNpbmdsZSB2ZWN0b3IgYW5kIHRoZW4gYWRkcyBpdCB0byB0aGUgY3JlYXRlIHZhcmlhYmxlIGBseXJpY3MyYC4KCmBgYHtyLCBldmFsID0gRn0gCnBqIDwtIG11dGF0ZShwaiwgbHlyaWNzMiA9ICIiKQpmb3IgKGVsZW1lbnQgaW4gKDE6bnJvdyhwaikpKSB7CiAgCiAgIyBJIGNyZWF0ZWQgdGhlIGxvb3Agd2l0aCB0d28gY2ljbGVzIGluIGl0IGluIG9yZGVyIHRvIGRvdWJsZSBjaGVjayBhbGwgdGhlIHNvbmdzIAogICMgZ2V0IHRoZWlyIGNvcnJlc3BvbmRpbmcgbHlyaWNzLiBOb3RlIHRoYXQgdGhlIGxvb3AgaXNvbGF0ZXMgZWFjaCBzb25nIGFuZAogICMgcmV0cmlldmVzIHRoZSBpbmZvcm1hdGlvbiB3aXRoIHRoZSBmdW5jdGlvbiBldF9seXJpY3Nfc2VhcmNoKCkKICAKICB0aXRsZSA8LSBzdHJfdG9fdGl0bGUocGokdHJhY2tfbmFtZVtlbGVtZW50XSkgCiAgcHJpbnQodGl0bGUpCiAgbHlyaWNzIDwtIGdldF9seXJpY3Nfc2VhcmNoKGFydGlzdF9uYW1lID0gIlBlYXJsIEphbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvbmdfdGl0bGUgPSB0aXRsZSkKICBpZiAobnJvdyhseXJpY3MpIT0wKSB7CiAgICAjIEkgb3B0ZWR0byByZWR1Y2UgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIHJldHJpZXZlZCBkYXRhIHNldCB0byAgb25lIG9ic2VydmF0aW9uLgogICAgIyBOb3cgSSBzYXZlIHRoZSBmaXJzdCBsaW5lIGFuZCBhZGQgdGhlIHJlc3Qgb2YgdGhlIGxpbmVzIG9mIHRoZSBzb25nCiAgbHlyaWNzMiA8LSAiIgogICAgZm9yIChwaWVjZSBpbiAoMTpucm93KGx5cmljcykpKSB7CiAgICBpZiAocGllY2UgPT0xKSB7CiAgICAgIGx5cmljczIgPC0gbHlyaWNzJGxpbmVbcGllY2VdCiAgICB9CiAgICBlbHNlewogICAgICBseXJpY3MyIDwtIHBhc3RlKGx5cmljczIsIGx5cmljcyRsaW5lW3BpZWNlXSwgY29sbGFwc2UgPSAiICIpCiAgICB9CiAgICBwcmludCh0aXRsZSkKICAgIHByaW50KGx5cmljczIpCiAgICB9CiAgfQogIAogICMgSWYgYW55IHNvbmcgaGFkIGFueSBwcm9ibGVtIHdpdGggcXVlcnlpbmcgdGhlIGx5cmljcyB0aGUgc2Vjb25kIHBhcnQgb2YgdGhlIGxvb3AKICAjIHJlcGVhdHMgdGhlIHByb2NjZXMgdG8gZ3VhcmFudGVlIHRoYXQgYWxsIHNvbmdzIGFyZSBhc3NpbmdlZCB0byB0aGVpciBseXJpY3MuCiAgCiAgaWYgKG5yb3cobHlyaWNzKT09MCkgewogICAgbHlyaWNzIDwtIGdldF9seXJpY3Nfc2VhcmNoKGFydGlzdF9uYW1lID0gIlBlYXJsIEphbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29uZ190aXRsZSA9IHRpdGxlKQogICAgbHlyaWNzMiA8LSAiIgogICAgZm9yIChwaWVjZSBpbiAoMTpucm93KGx5cmljcykpKSB7CiAgICAgIGlmIChwaWVjZSA9PTEpIHsKICAgICAgICBseXJpY3MyIDwtIGx5cmljcyRsaW5lW3BpZWNlXQogICAgICB9CiAgICAgIGVsc2V7CiAgICAgICAgbHlyaWNzMiA8LSBwYXN0ZShseXJpY3MyLCBseXJpY3MkbGluZVtwaWVjZV0sIGNvbGxhcHNlID0gIiAiKQogICAgICB9CiAgICAgIHByaW50KHRpdGxlKQogICAgICBwcmludChseXJpY3MyKQogICAgfQogIH0KICAKICAjIEZpbmFsbHkgdGhlIHNvbmcgaXMgYWRkZWQgdG8gdGhlIG9yaWdpbmFsIGRhdGEKICAKICBwaiRseXJpY3MyW2VsZW1lbnRdIDwtIGx5cmljczIKfQpgYGAKCk91ciBkYXRhIHNldCBub3cgY29udGFpbnMgbHlyaWNzIHRvIGFsbCB0aGUgMTQ2IHNvbmdzIGluIHRoZSAxMSBhbGJ1bXMuIEEgZ2xpbXBzZSB0byBteSB0b3AgdHdvIFBlYXJsIEphbSBzb25ncyAoYWNjb3JkaW5nIHRvIHNwb3RpZnkgcmFua2luZ3MpIEJsYWNrIGFuZCBFdmVuIGZsb3c6CgpgYGAge3IsIGV2YWw9VCAsaW5jbHVkZT1GfSAKcGokdHJhY2tfbmFtZSA8LSBzdHJfdG9fdGl0bGUocGokdHJhY2tfbmFtZSkKYGBgCgpgYGAge3J9CnNvbmdzIDwtIGMoJ0JsYWNrJywgJ0V2ZW4gRmxvdycpCnBqW3BqJHRyYWNrX25hbWUgJWluJSBzb25ncyxdICU+JSBzZWxlY3QoYXJ0aXN0X25hbWUsIGFsYnVtX25hbWUsIHRyYWNrX25hbWUsIGx5cmljcykgJT4lIAogIGthYmxlKGZvcm1hdCA9ICJzaW1wbGUiLGNvbC5uYW1lcyA9IHN0cl90b190aXRsZShnc3ViKCJbX10iLCAiICIsIGNvbG5hbWVzKC4pKSksYWxpZ24gPSAnbGNjY2NjY2NjYycpCmBgYAoKRmluYWxseSwgd2UgaGF2ZSB0byB0YWtlIGludG8gYWNjb3VudCBpbnN0dW1lbnRhbCBzb25ncy4gRXZlbiB0aG91Z2ggUGVhcmwgSmFtIGRvZXNuJ3QgaGF2ZSBtYW55IGluc3RydW1lbnRhbCBzb25ncyAzIGNhc2VzIG5lZWQgdG8gYmUgYWRyZXNzZWQuIHRoZXNlIGFyZSAqKkFyYyoqLCAqKkF5YSBEYXZhbml0YSoqIGFuZCAqKkNyZWFkeSBTdG9tcCoqLiBJIHByb2NlZCB0aGVuIHRvIGFzc2luZyBhIG1pc3NpbmcgdmFsdWUgdG8gdGhlc2Ugc29uZ3MgaW4gb3JkZXIgdG8gaW1wbGVtZW50IHRoZSBmb2xsb3dvbmcgYW5hbHlzaXMuCmBgYCB7ciwgZXZhbCA9IFR9CnNvbmdzIDwtIGMoIkFyYyIsIkF5YSBEYXZhbml0YSAtIFJlbWFzdGVyZWQiLCJDcmVhZHkgU3RvbXAgLSBCb251cyBUcmFjayIpCnBqW3BqJHRyYWNrX25hbWUgJWluJSBzb25ncyxncmVwKCdseXJpY3MnLCBjb2xuYW1lcyhwaikpXQpwaltwaiR0cmFja19uYW1lICVpbiUgc29uZ3MsZ3JlcCgnbHlyaWNzJywgY29sbmFtZXMocGopKV0gPC0gTkEKYGBgCgojIERhdGEgZXhwbG9yYXRpb24KIyMgVG9rZW5zICYgZGF0YSB3cmFuZ2xpbmcKClRoZSBkYXRhc2V0IGlzIGFsbW9zdCByZWFkeSBmb3IgdXNlLiBJIG5vdyBleGVjdXRlIHNvbWUgZmV3IGRhdGEgd3JhbmdsaW5nIHN0ZXBzIGluIG9yZGVyIHRvIGhhdmUgYWxsIHRoZSBpbmZvcm1hdGlvbiBuZWVkZWQuIEkgdXNlIHRoZSBwYWNrYWdlIGB0bWAgaW4gb3JkZXIgdG8gcmVtb3ZlIHB1bmN0dWF0aW9uLCBzdG9wIHdvcmRzIGFuZCBzdGVtbWluZy4gSSBiZWdpbiBieSBjb21waWxpbmcgYWxsIGx5cmljcyBhbmQgY3JlYXRlIGEgY29ycHVzIHVzaW5nIHRoZSBmdW5jdGlvbiBgVmVjdG9yU291cmNlYCB0byByZWFkIGl0IGEgcyBhIGRvY3VtZW50IGFuZCB0aGVuIHBhc3MgdGhlIGRvY3VtZW50cyB0aHJvdWdoIHRoZSBgQ29ycHVzYCB0byBjcmVhdGUgdGhlIGZpbmFsIGNvcnB1cy4gTm90ZSB0aGF0IGFsbCB0aGUgZG9jdW1lbnQgdHJhbnNmb3JtYXRpb24gb2YgdGhlIGRvY3VtZW50IGFyZSBkb25lIHRocm91Z2ggdGhlIGZ1bmNpdG9uIGB0bV9tYXBgLCBhcHBseWluZyBhIGZ1bmN0aW9uIHRvIGFsbCBlbGVtZW50cyBvZiB0aGUgZG9jdW1lbnQuIFRoZSBwcm9jZXNzIEkgZm9sbG93IGlzOiAqKjEpKiogdHJhbnNmb3JtIGFsbCB1cHBlci1jYXNlIGxldHRlciB0byBsb3dlci1jYXNlOyAqKjIpKiogcmVtb3ZlIGFsbCBwdW5jdGlvbmF0aW9uOyAqKjMpKiogcmVtb3ZlIEVuZ2xpc2ggc3RvcCB3b3JkczsgYW5kICoqNCkqKiBzdGVtIGFsbCB0aGUgd2hpdGUgc3BhY2VzLgoKYGBgIHtyLCBldmFsPVQsaW5jbHVkZSA9IEZ9Cmx5cmljcyA8LSBDb3JwdXMoVmVjdG9yU291cmNlKHBqJGx5cmljcykpCmx5cmljcyA8LSB0bV9tYXAobHlyaWNzLCBjb250ZW50X3RyYW5zZm9ybWVyKHRvbG93ZXIpKQpseXJpY3MgPC0gdG1fbWFwKGx5cmljcywgcmVtb3ZlUHVuY3R1YXRpb24pCmx5cmljcyA8LSB0bV9tYXAobHlyaWNzLCByZW1vdmVXb3Jkcywgc3RvcHdvcmRzKCJlbmdsaXNoIikpCmx5cmljcyA8LSB0bV9tYXAobHlyaWNzLCBzdHJpcFdoaXRlc3BhY2UpCmx5cmljcyA8LSBseXJpY3MgJT4lIHVubGlzdCgpCnBqJGx5cmljcyA8LSBhcy5jaGFyYWN0ZXIobHlyaWNzWzE6bnJvdyhwaildKQpwaiRseXJpY3MgPC0gc3RyX3RyaW0ocGokbHlyaWNzKQoKYGBgCgpgYGAge3IsIGV2YWw9RixpbmNsdWRlID0gVH0KbHlyaWNzIDwtIENvcnB1cyhWZWN0b3JTb3VyY2UocGokbHlyaWNzKSkKbHlyaWNzIDwtIHRtX21hcChseXJpY3MsIGNvbnRlbnRfdHJhbnNmb3JtZXIodG9sb3dlcikpCmx5cmljcyA8LSB0bV9tYXAobHlyaWNzLCByZW1vdmVQdW5jdHVhdGlvbikKbHlyaWNzIDwtIHRtX21hcChseXJpY3MsIHJlbW92ZVdvcmRzLCBzdG9wd29yZHMoImVuZ2xpc2giKSkKbHlyaWNzIDwtIHRtX21hcChseXJpY3MsIHN0cmlwV2hpdGVzcGFjZSkKbHlyaWNzIDwtIGx5cmljcyAlPiUgdW5saXN0KCkKcGokbHlyaWNzIDwtIGFzLmNoYXJhY3RlcihseXJpY3NbMTpucm93KHBqKV0pCnBqJGx5cmljcyA8LSBzdHJfdHJpbShwaiRseXJpY3MpCgpgYGAKCkFmdGVyIHByb2Nlc2luZyB0aGUgbHlyaWNzIHRoZXJlJ3Mgc3RpbGwgc29tZSBzcGVjaWFsIGNhc2VzIHRoYXQgbmVlZCB0byBiZSB0YWtlbiBpbnRvIGFjY291bnQgaXIgb3JkZXIgdG8gcHJldmVudCBiaWFzIGluIHRoZSByZXN1bHRzLiBGaXJzdCwgd29yZCBjb250cmFjdGlvbnMgbmVlZCB0byBiZSBhZGRyZXNzZWQuIEFzIEkgcmVtb3ZlZCBhbGwgcHVuY3R1YXRpb24gYmVmb3JlLCBzb21lIGNvbnRyYWN0aW9ucyBzdWNoIGFzICoqc2hlJ3MqKiB3aWxsIGJlIG5vdyAqKnNoZXMqKiBhbmQgYXJlIG5vdCB0YWtlbiBhcyBzdG9wIHdvcmRzIGJ5IHRoZSBmdW5jdGlvbiBgdG1fbWFwYC4gT3RoZXIgY2FzZXMgYXJlIHJ5dGhtaXRpYyB2b2NhbHMuIFRha2UgQmxhY2sgYXMgYW4gZXhhbXBsZS4gVGhlIGxhc3QgbWludXRlIG9mIHRoZSBzb25nIEVkZGllIGdvZXMgb24gbGlrZSAqKmRvbyBkb28gZG9vIGRvbyBkb28gZG9vIGRvbyoqLiBJbiBvcmRlciB0byBwcmV2ZW50IGJpYXNlZCByZXN1bHRzIEkgb21taXQgYWxsIHJ5dGhtaXRpYyB2b2NhbHMgYW5kIHJlcGxhY2UgdGhlIGNvbnRyYWN0aW9ucyBieSB0aGVpciByZXNwZWN0aXZlIGpvaW50IHdvcmRzLgoKYGBgIHtyLCBldmFsID0gVH0KCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnYm90dG9tb2gnLCAnIGJvdHRvbSBvaCAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyBoZXlpbSAnLCAnIGhleSBJIGFtICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIGRlZXBvaCAnLCAnIGRlZXAgb2ggJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgYnV0dGVyZmxpZXNkb250ICcsICcgYnV0dGVyZmxpZXMgZG8gbm90ICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIGF3YXlzb21lZGF5ICcsICcgYXdheSBzb21lZGF5ICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIHJpc2luZ25leHQgJywgJyByaXNpbmcgbmV4dCAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyB1aG9ob2ggJywgJyBvaCAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyBhbmdlbGxlc3QgJywgJyBhbmdlbCBsZXRzICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIGRvb2Rvb2Rvb2Rvb2Rvb2Rvb2RvbyAnLCAnJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgeWVhaCAnLCAnICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIHllYWgnLCAnICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIG9vaCAnLCAnICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIG9oICcsICcgJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgYWggJywgJyAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyBobSAnLCAnICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIHllICcsICcgJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgYXkgJywgJyAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyB5ZWggJywgJyAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyB5YSAnLCAneW91JykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgbW0gJywgJyAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyBpbnRlcmx1ZGUgJywgJyAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyB3b28gJywgJyAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyBtaG0gJywgJyAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyBvb29oICcsICcgJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgb29vb2ggJywgJyAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyBoZXkgJywgJyAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyB1aCAnLCAnICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIHlvdWxsICcsICcgeW91IHdpbGwgJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgY2FudCAnLCAnIGNhbiBub3QgJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICdjYW50ICcsICcgY2FuIG5vdCAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyBpbSAnLCAnIEkgYW0gJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgaWxsICcsICcgSSB3aWxsICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIGRpZG50ICcsICcgZGlkIG5vdCAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyBkb250ICcsICcgZG8gbm90ICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIGhlcyAnLCAnIGhlIGlzICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIHVoaHVoICcsICcnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyBpdHMnICwgJycpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIGlkICcsICcgSSB3b3VsZCAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyB5b3VyZSAnLCAnIHlvdSBhcmUgJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgeW91dmUgJywgJyB5b3UgaGF2ZSAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyB5b3VkICcsICcgeW91IHdvdWxkICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIHRoZXJlcyAnLCAnIHRoZXJlIGlzICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIHRoZXlsbCAnLCAnIHRoZXkgd2lsbCAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyB3aGF0cyAnLCAnIHdoYXQgaXMgJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgZG9lc250ICcsICcgZG9lcyBub3QgJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgaXZlICcsICcgSSBoYXZlICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIGhhZG50ICcsICcgaGFkIG5vdCAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyB3b3VsZG50ICcsICcgd291bGQgbm90ICcpCnBqJGx5cmljcyA8LSBzdHJfcmVwbGFjZV9hbGwocGokbHlyaWNzLCAnIHRpbCAnLCcgdW50aWwgJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgd291bGR2ZSAnLCAnIHdvdWxkIGhhdmUgJykKcGokbHlyaWNzIDwtIHN0cl9yZXBsYWNlX2FsbChwaiRseXJpY3MsICcgc2hlcyAnLCAnIHNoZSBpcyAnKQpwaiRseXJpY3MgPC0gc3RyX3JlcGxhY2VfYWxsKHBqJGx5cmljcywgJyB0aGF0cyAnLCAnIHRoYXQgaXMgJykKYGBgCgpPbmUgbGFzdCBzdGVwIGludG8gb3VyIGZpbmFsIGRhdGFzZXQgaXMgd29yZCBzZW50aW1lbnQuIEkgdGhlbiB0dXJuIHRoZSBkYXRhIGludG8gYSB0aWR5IGZvcm1hdCB1c2luZyB0aGUgcGFja2FnZSBgdGlkeXRleHRgLiBJIHVzZSB0aGUgZnVuY3Rpb24gYHVubmVzdF90b2tlbnNgIGluIG9yZGVyIHRvIHNwbGl0IHRoZSBseXJpY3MgaW50byBpbmRpdnVkdWFsIHdvcmRzLiB0aGUgZnVuY3Rpb24gYGdldF9zZW50aW1lbnRzYCBpcyB1c2VkdG8gb2J0YWluIHRoZSBzZW50aW1lbnRzIGxleGljb24gaW4gYSB0aWR5IGZvcm1hdC4gVGhlIGxleGljb24gdXNlZCBpcyBgTkNSYCAqKldvcmQtRW1vdGlvbiBBc3NvY2lhdGlvbiBMZXhpY29uKiouIFRoaXMgbGV4aWNvbiBsaXN0cyBFbmdsaXNoIHdvcmRzIGFuZCBtYXRjaGVzIHRoZWlyIGFzc29jaWF0aW9ucyB3aXRoIGVpZ2h0IGJhc2ljIGVtb3Rpb25zIChhbmdlciwgZmVhciwgYW50aWNpcGF0aW9uLCB0cnVzdCwgc3VycHJpc2UsIHNhZG5lc3MsIGpveSwgYW5kIGRpc2d1c3QpIGFuZCB0d28gc2VudGltZW50cyAobmVnYXRpdmUgYW5kIHBvc2l0aXZlKS4gRnVydGhlciBpbmZvcm1hdGlvbiBjb3VsZCBiZSBmb3VuZCBpbiB0aGUgb2ZmaWNpYWwgW3dlYiBwYWdlXShodHRwczovL3NhaWZtb2hhbW1hZC5jb20vV2ViUGFnZXMvTlJDLUVtb3Rpb24tTGV4aWNvbi5odG0pLiBXaXRoIHRoZSAgc2VudGltZW50cyBtYXRjaGVkIEknbSBhYmxlIHRvIGNvdW50IHRoZSBudW1iZXIgb2Ygd29yZHMgcGVyIGVtb3Rpb24gYW5kIHNlbnRpbWVudC4KYGBgIHtyLCBldmFsID1GLCBpbmNsdWRlID0gVH0KCnBlYXJsX3NvbmdzIDwtIHBqICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsIGx5cmljcykgIyBTZXBhcmF0ZSB0aGUgbHlyaWNzIGludG8gc2luZ2xlIHdvcmRzCmNsZWFuZWRfcGVhcmwgPC0gcGVhcmxfc29uZ3MgJT4lIGFudGlfam9pbihzdG9wX3dvcmRzKSAjIEVsaW1pbmF0aW5nIHN0b3Agd29yZHMKbnJjIDwtIGdldF9zZW50aW1lbnRzKGxleGljb24gPSAibnJjIikgIyBDYXRlZ29yaWNhbCB3b3JkcyBzZW50aW1lbnRzCgpwZWFybF9zZW50aW1lbnQgPC0gY2xlYW5lZF9wZWFybCAlPiUKICBpbm5lcl9qb2luKG5yYyxieSA9IGMoJ3dvcmQnKSkgJT4lICMgTWVyZ2luZyB0aGUgd29yZCBzZW50aW1lbnQgZGF0YSB3aXRoIHRoZSB3b3JkcyBkYXRhc2V0CiAgY291bnQodHJhY2tfbmFtZSwgc2VudGltZW50KSAlPiUgIyBDb3VudGluZyB0aGUgbnVtYmVyIG9mIHdvcmRzIHBlciBzZW50aW1lbnQKICBzcHJlYWQoc2VudGltZW50LCBuLCBmaWxsID0gMCkgIyBSZXNoYXBpbmcgdGhlIGRhdGEgZnJvbSBvYnNlcnZhdGlvbnMgaW50byBjb2x1bW5zCgpjb2x1bW5zIDwtIGMoZ3JlcCgnYWxidW1fbmFtZScsIGNvbG5hbWVzKHBqKSksCiAgICAgICAgICAgICBncmVwKCd0cmFja19uYW1lJywgY29sbmFtZXMocGopKSkKCnBlYXJsX3NlbnRpbWVudCA8LSBpbm5lcl9qb2luKHggPSBwZWFybF9zZW50aW1lbnQsIHkgPSBwalssY29sdW1uc10sIGJ5ID0gInRyYWNrX25hbWUiKQpwaiA8LSBpbm5lcl9qb2luKHBqLCBwZWFybF9zZW50aW1lbnQpCgpgYGAKCmBgYCB7ciwgZXZhbCA9VCxpbmNsdWRlID0gRn0KCnBlYXJsX3NvbmdzIDwtIHBqICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsIGx5cmljcykKY2xlYW5lZF9wZWFybCA8LSBwZWFybF9zb25ncyAlPiUgYW50aV9qb2luKHN0b3Bfd29yZHMpCm5yYyA8LSBnZXRfc2VudGltZW50cyhsZXhpY29uID0gIm5yYyIpCiMgCnBlYXJsX3NlbnRpbWVudCA8LSBjbGVhbmVkX3BlYXJsICU+JQogIGlubmVyX2pvaW4obnJjLGJ5ID0gYygnd29yZCcpKSAlPiUKICBjb3VudCh0cmFja19uYW1lLCBzZW50aW1lbnQpICU+JQogIHNwcmVhZChzZW50aW1lbnQsIG4sIGZpbGwgPSAwKQoKY29sdW1ucyA8LSBjKGdyZXAoJ2FsYnVtX25hbWUnLCBjb2xuYW1lcyhwaikpLAogICAgICAgICAgICAgZ3JlcCgndHJhY2tfbmFtZScsIGNvbG5hbWVzKHBqKSkpCgpwZWFybF9zZW50aW1lbnQgPC0gaW5uZXJfam9pbih4ID0gcGVhcmxfc2VudGltZW50LCB5ID0gcGpbLGNvbHVtbnNdLCBieSA9ICJ0cmFja19uYW1lIikKcGogPC0gaW5uZXJfam9pbihwaiwgcGVhcmxfc2VudGltZW50KQoKYGBgCgojIyBHbG9vbSBJbmRleAoKV2l0aCB0aGUgZmluYWwgZGF0YXNldCBJJ20gYWJsZSB0byBkbyBzb21lIHNvcnQgb2YgYW5hbHlzaXMgdG8gUGVhcmwgSmFtJ3MgZGlzY29ncmFwaHkuIEF0IGZpcnN0IGhhbmQgSSB3YXMgaW50ZXJlc3RlZCBpbiBmaW5kaW5nIHNvbWUgbWV0cmljIG9mIHNhZG5lc3MgaW4gYSBtdXNpY2FsIGNvbnRleHQuIE9uZSBvZiB0aGUgbW9zdCBpbnRlcmVzdGluZyBhbmFseXNpcyB0YWtpbmcgdGhpcyBpbnRvIGFjY291bnQgaXMgdGhlIFtHbG9vbSBJbmRleF0oaHR0cHM6Ly93d3cucmNoYXJsaWUuY29tL2Jsb2cvZml0dGVyLWhhcHBpZXIvKSBkZXZlbG9wZWQgYnkgW0NoYXJsaWUgVGhvbXBzb25dKGh0dHBzOi8vd3d3LnJjaGFybGllLmNvbSkuIEhlcmUgdGhlIGF1dGhvciB1c2VzIHRoZSBpbmZvcm1hdGlvbiBvbiB0aGUgdmFsZW5jZSBvZiB0aGUgc29uZywgdGhlIHBlcmNlbnRhZ2Ugb2Ygc2FkIHNvbmdzIGFuZCB0aGUgbHlyaWNhbCBkZW5zaXR5IG9mIHRoZSBzb25nLiBJbiB0ZXJtcyBvZiB2YWxlbmNlLCBTcG90aWZ5IHVzZXMgYSB3aWRlIHZhcmlldHkgb2YgaW5wdXRzIHRvIGRldGVybWluZSB3ZWF0aGVyIGEgc29uZ3Mgc291bmQgaGFwcHkgb3Igbm90LiBXaXRoIHRoaXMgaW4gbWluZCBzYWQgc29uZ3MgaGF2ZSBhIGxvd2VyIHZhbGVuY2Ugc2NvcmUgYW5kIGhhcHB5IHNvbmdzIGhhdmUgaGlnaCB2YWxlbmNlLiBIZXJlJ3MgYSBwcmV2aWV3IG9mIHNvbWUgb2YgUGVhcmwgSmFtJ3Mgc2FkZGVzdCBzb25ncyBhY2NvcmRpbmcgdGhpcyBtZWFzdXJlOgoKYGBge3J9CnBqICU+JSBncm91cF9ieSh0cmFja19uYW1lKSAlPiUgc3VtbWFyaXNlKHZhbGVuY2UgPSBtZWFuKHZhbGVuY2UpKSAlPiUgYXJyYW5nZSh2YWxlbmNlKSAlPiUgaGVhZCgxMCkgJT4lIGthYmxlKGZvcm1hdCA9ICJzaW1wbGUiLCBjb2wubmFtZXMgPSBzdHJfdG9fdGl0bGUoZ3N1YigiW19dIiwgIiAiLCBjb2xuYW1lcyguKSkpLGFsaWduID0gJ2xjJyxjYXB0aW9uID0gIlRvcCB0ZW4gc2FkZGVzdCBQZWFybCBKYW0gc29uZ3MgLSBWYWxlbmNlIHNjb3JlIixkaWdpdHMgPSAzKQpgYGAKCk9yaWdpbmFsbHkgdGhlIGluZGV4IHRha2VzIHRoZSBmb2xsb3dpbmcgZm9ybToKCiQkR2xvb21cO0luZGV4ID0gXGZyYWN7KDEtdmFsZW5jZSkgKyBwZXJjZW50YWdlXF9zYWQoMStseXJpY2FsXF9kZW5zaXR5KX17Mn0kJApOb3csIG5vdGluZyB0aGF0IHRoZSBseXJpY2FsIGRlbnNpdHkgbWlndGggYmUgbWlzbGVhZGluZyBJIHR1cm4gdG8gb3RoZXIgbWVhc3VyZSBvZiBseXJpY2FsIGRlbnNpdHkuIExvbmdlciBzb25ncyBtaWdodCBoYXZlIG1vcmUgcmVwZWF0ZWQgbHlyaWNzIHRoYW4gc2hvcnRlciBzb25ncy4gVGhpcyBjYW4gYmUgZWl0aGVyIGJ5IG1vcmUgY2hvcnVzZXMgb3IgYnJpZGdlcywgYnV0IHRoZSByZXN1bHRzIG1pZ2h0IGJlIHdyb25nIGZvciB0aGlzIHJlYXNvbi4gQWNjb3VudGluZyBmb3IgdGhpcyBwcm9ibGVtIGEgY2xlYW5lciBtZXRyaWMgZm9yIGx5cmljYWwgZGVuc2l0eSBpcyB0aGUgcmF0aW8gb2YgZGlzdGljbnQgd29yZHMgYW5kIHRvdGFsIHdvcmRzIHVzZWQuCiQkR2xvbW1cOyBJbmRleCA9IFxmcmFjeygxLXZhbGVuY2UpK3BlcmNlbnRhZ2VcX3NhZCgxK2Rpc3RpbmN0XF9wZXJjZW50YWdlKX17Mn0kJCAKCldoZXJlCgokJGRpc3RpbmN0XF9wZXJjZW50YWdlID0gXGZyYWN7XCMgZGlzdGluY3QgXDt3b3Jkc317XCMgdG90YWwgd29yZHN9JCQKCk5vdywgaGF2aW5nIHRoaXMgaW5mb3JtYXRpb24gaG93IGNhbiB0aGlzIGluZGV4IGJlIGludGVycHJldGVkPyBXZSBuZWVkIHRvIHVuZGVyc3RhbmQgaG93IGdsb29tIG1vdmVzIGFsb25nIHRoZSBkaWZmZXJlbnQgaW5wdXRzLiBGaXJzdCwgdmFsZW5jZSBzaG93cyBoaWdoZXIgdmFsdWVzIGZvciBoYXBweSBzb25ncyB3aGlsc3QgbG93IHZhbHVlcyBmb3Igc2FkIHNvbmdzLiBOb3csIGJ5IHN1YnRyYWN0aW5nIHRoZSB2YWxlbmNlLCBhIHNvbmcgd2l0aCBsb3dlciB2YWxlbmNlIHdpbGwgaGF2ZSBhIGhpZ2hlciBpbmRleCBzY29yZS4gSW4gb3RoZXIgd29yZHMsIHZhbGVuY2UgaGFzIGFuIGludmVyc2UgcmVsYXRpb25zaGlwIHdpdGggdGhlIGdsb29tIGluZGV4LiBOb3csIHR1cmJ1bGVudCB3b3JkcyBhbmQgZGlzdGluY3Qgd29yZHMgcGVyY2VudGFnZSBsb2dpY2FsbHkgbW92ZSBpbiB0aGUgc2FtZSBkaXJlY3Rpb24gb2YgdGhlIGdsb29tIGluZGV4LiBJIGNhbiBub3cgc2F5IHRoYXQgKioqaGlnaGVyIHZhbHVlcyBzaG93IHNhZGRlciBzb25ncywgd2hpbGUgbG93ZXIgdmFsdWVzIHJlcHJlc2VudCBoYXBwaWVyIHNvbmdzKioqLiBOb3RlIHRoYXQgY29tcGFyZWQgdG8gQ2hhcmxpZSdzIFtSYWRpb2hlYWQgYW5hbHlzaXNdKGh0dHBzOi8vd3d3LnJjaGFybGllLmNvbS9ibG9nL2ZpdHRlci1oYXBwaWVyLykgbXkgaW5kZXggaXMgaW50ZXJwcmV0ZWQgaW4gYSBjb21wbGV0ZWx5IGRpZmZlcmVudCB3YXkuIFRoZSBtYWluIHJlYXNvbiBpcyBiZWNhdXNlIENoYXJsaWUgcmVzY2FsZXMgaGlzIGluZGV4IGluIG9yZGVyIHRvIHJlcHJlc2VudCBoYXBweSBzb25ncyB3aXRoIGhpZ2hlciB2YWx1ZXMuIE5vdywgdGhlIGludGVycHJldGF0aW9uIG9mIHRoZSByZXN1bHRzIGFyZSBhbmFsb2dvdXMsIHRoZSBvbmx5IGRpZmZlcmVuY2UgYXJlIHRoZSBzY2FsZSBpbiB3aGljaCB0aGV5IGFyZSBwcmVzZW50ZWQuCgpgYGB7ciwgZXZhbCA9IFR9CnBqIDwtIHBqICU+JSB1bm5lc3RfdG9rZW5zKHdvcmQsIGx5cmljcykgJT4lCiAgZ3JvdXBfYnkodHJhY2tfbmFtZSkgJT4lIHN1bW1hcmlzZShkaXN0aW5jdF93b3JkcyA9IG5fZGlzdGluY3Qod29yZCkpICU+JSAKICBpbm5lcl9qb2luKHBqKQoKcGogPC0gbXV0YXRlKHBqLCB3b3JkcyA9IHNhcHBseShzdHJzcGxpdChwaiRseXJpY3MsICIgIiksIGxlbmd0aCkpCnBqIDwtIG11dGF0ZShwaiwgc2Vjb25kcyA9IGR1cmF0aW9uX21zLzEwMDApCnBqIDwtIG11dGF0ZShwaiwgd29yZHNfcGVyX3NlY29uZCA9IHdvcmRzL3NlY29uZHMpCnBqIDwtIG11dGF0ZShwaiwgc2FkX3BlcmNlbnRhZ2UgPSBzYWRuZXNzL2Rpc3RpbmN0X3dvcmRzKQpwaiA8LSBtdXRhdGUocGosIGRpc3RfcGVyID0gKGRpc3RpbmN0X3dvcmRzL3dvcmRzKSkKcGogPC0gbXV0YXRlKHBqLCBpbmRleCA9ICgoMS12YWxlbmNlKStzYWRfcGVyY2VudGFnZSooMStkaXN0X3BlcikpLzIpCmBgYAoKCkkgY2FuIG5vdyB2aXp1YWxpemUgZGlmZmVyZW50IGluZm9ybWF0aW9uIEkgaGF2ZSBnYXRoZXJlZCBhbG9uZyB0aGUgZG9jdW1lbnQuIEZpcnN0LCB3ZSBjYW4gc2VlIHRoZSBzYWRkZXN0IHNvbmdzIGFjY29yZGluZyB0byB0aGUgR2xvb20gSW5kZXguIAoKCmBgYHtyLCBldmFsID0gVH0KcGogJT4lIGdyb3VwX2J5KHRyYWNrX25hbWUsYWxidW1fbmFtZSkgJT4lIAogIHN1bW1hcmlzZShpbmRleCA9IG1lYW4oaW5kZXgpLAogICAgICAgICAgICB2YWxlbmNlID0gbWVhbih2YWxlbmNlKSwKICAgICAgICAgICAgYHR1cmJ1bGVudCB3b3Jkc2AgPSBtZWFuKHNhZF9wZXJjZW50YWdlKSwKICAgICAgICAgICAgYGRpc3RpbmN0IHBlcmNlbnRhZ2Ugd29yZHNgID0gbWVhbihkaXN0X3BlcikpICU+JSBhcnJhbmdlKGRlc2MoaW5kZXgpKSAlPiUgaGVhZCgxMCkgJT4lIGthYmxlKGZvcm1hdCA9ICJzaW1wbGUiLCBjb2wubmFtZXMgPSBzdHJfdG9fdGl0bGUoZ3N1YigiW19dIiwgIiAiLCBjb2xuYW1lcyguKSkpLGFsaWduID0gJ2xjY2NjYycsY2FwdGlvbiA9ICJUb3AgdGVuIHNhZGRlc3QgUGVhcmwgSmFtIHNvbmdzIC0gR2xvb20gSW5kZXgiLGRpZ2l0cyA9IDMpCmBgYAoKV2UgaGF2ZSBub3cgdGhlIG1vc3QgZGVwcmVzc2luZyBQZWFybCBKYW0gc29uZyB0byBkYXRlOiAqKlNwaW4gdGhlIEJsYWNrIENpcmNsZSoqLiBCdXQgaG93IGNhbiBpdCBiZT8gQW55b25lIHdobyBoYXZlIGhlYXJkIHRoZSByaWd0aCBhbW1vdW50IG9mIHRoZWlyIHNvbmdzIGNvdWxkIGFyZ3VlIGluIGZhdm9yIG9mIFJlbGVhc2Ugb3IgQWxsIHRob3NlIHllc3RlcmRheXMsIG9yIGV2ZW4gQmxhY2ssIGJ1dCByZW1lbWJlciB0aGF0IHRoaXMgaXMgYSBkYXRhIGRyaXZlbiBtZWFzdXJlLiBTcGluIHRoZSBCbGFjayBDaXJjbGUgYWN0dWFsbHkgaGFzIGEgcmF0aGVyIGxvdyB2YWxlbmNlIHNjb3JlIChtZWFuaW5nIGlzIGEgc2FkIHNvbmcpLCByYW5raW5nIDE5dGggd2l0aCAwLjE0MS4gQnV0IHRoZSBwZXJjZW50YWdlIG9mIHNhZCB3b3JkcyBpcyB0aGUgc2Vjb25kIGhpZ2hlc3Qgb2YgYWxsIHNvbmdzLCBiZWhpbmQgUmVkIEJhciAod2hpY2ggcmVwZWF0cyBzZXZlbiB0aW1lcykgd2loIGEgcGVyY2VudGFnZSBvZiBzYWQgd29yZCBvZiA1MiUuIElmIHRoZSByYW5raW5nIHNlZW1zIG9kZCBsaXN0ZW4gdG8gdGhlIHNvbmdzIGFuZCBzZWUgaXQgZm9yIHlvdXJzZWxmLgoKVG8gc2VlIGhvdyBzYWRuZXNzIG9yIGdsb29tIGhhcyBldm9sdmVkIGFjcm9zcyB0aW1lLCBpcyBwb3NpYmxlIHRvIGF2ZXJhZ2UgdGhlIGluZGV4IHBlciBhbGJ1bSBhbmQgc2VlIHRoZSBkeW5hbWljLiBUaGlzIGlzIHRoZSByYW5raW5nIGZvciB0aGUgc2FkZGVzdCBhbGJ1bXM6CgpgYGB7ciwgZXZhbCA9IFR9CnBqICU+JSBncm91cF9ieShhbGJ1bV9uYW1lKSAlPiUgCiAgc3VtbWFyaXNlKGluZGV4ID0gbWVhbihpbmRleCksCiAgICAgICAgICAgIHZhbGVuY2UgPSBtZWFuKHZhbGVuY2UpLAogICAgICAgICAgICBgc2FkIHdvcmRzYCA9IG1lYW4oc2FkX3BlcmNlbnRhZ2UpLAogICAgICAgICAgICBgZGlzdGluY3QgcGVyY2VudGFnZSB3b3Jkc2AgPSBtZWFuKGRpc3RfcGVyKSkgJT4lIGFycmFuZ2UoZGVzYyhpbmRleCkpICU+JSBoZWFkKDEyKSAlPiUga2FibGUoZm9ybWF0ID0gInNpbXBsZSIsIGNvbC5uYW1lcyA9IHN0cl90b190aXRsZShnc3ViKCJbX10iLCAiICIsIGNvbG5hbWVzKC4pKSksYWxpZ24gPSAnbGNjY2NjJyxjYXB0aW9uID0gIlNhZGRlc3QgUGVhcmwgSmFtIGFsYnVtcyAtIEdsb29tIEluZGV4IixkaWdpdHMgPSAzKQpgYGAKCgoKYGBge3IsIGluY2x1ZGUgPUYsIGV2YWwgPVR9CnJhbmsgPC0gIHBqICU+JSBncm91cF9ieShhbGJ1bV9uYW1lLHRyYWNrX25hbWUsYWxidW1fcmVsZWFzZV95ZWFyKSAlPiUgc3VtbWFyaXNlKGF2ZXJhZ2UgPSBtZWFuKGluZGV4KSkgJT4lIGFycmFuZ2UoYWxidW1fcmVsZWFzZV95ZWFyKQpyYW5rIDwtICBpbm5lcl9qb2luKHJhbmssIHJhbmsgJT4lIGdyb3VwX2J5KGFsYnVtX25hbWUpICU+JSBzdW1tYXJpc2UoZ2xvb21faW5kZXggPSBtZWFuKGF2ZXJhZ2UpKSkKcmFuayA8LSBhcy5kYXRhLmZyYW1lKHJhbmspCgphbGJ1bV9sZXZlbHMgPC0gYygKICAiVGVuIiwgIlZzLiIsICJWaXRhbG9neSIsICJObyBDb2RlIiwgIllpZWxkIiwgIkJpbmF1cmFsIiwgCiAgIlJpb3QgQWN0IiwgIlBlYXJsIEphbSIsICJCYWNrc3BhY2VyIiwgIkxpZ2h0bmluZyBCb2x0IiwgIkdpZ2F0b24iKQoKcmFuayRhbGJ1bV9uYW1lIDwtIGZhY3RvcihyYW5rJGFsYnVtX25hbWUsIGxldmVscyA9IGFsYnVtX2xldmVscykKcGokYWxidW1fbmFtZSA8LSBmYWN0b3IocGokYWxidW1fbmFtZSwgbGV2ZWxzID0gYWxidW1fbGV2ZWxzKQoKYGBgCgpOb3csIGl0IGlzIHBvc2libGUgdG8gaW5jbHVkZSBib3RoIHJlc3VsdHMgZnJvbSB0aGUgbGFzdCB0d28gdGFibGVzIGluIG9uZSBncmFwaCBpbiBvcmRlciB0byBjb21wYXJlIHRoZSByZXN1bHRzLiBFYWNoIHBvaW50IGluIHRoZSBncmFwaCByZXByZXNlbnRzIGEgc29uZ3MsIGFuZCBmb3IgZWFjaCBzb25nIHRoZXJlcyBhIGdsb29tIGluZGV4IHNjb3JlLiBUaGUgYmxhY2sgZG90cyByZXByZXNlbnQgdGhlIGF2ZXJhZ2Ugc2NvcmUgZm9yIGVhY2ggYWxidW0uIEl0IGlzIGltcHJvdGFudCB0byBub3RlIHRoYXQgYnkgdGhlIHRpbSBwZWFybCBqYW0gc29uZ3Mgc2VlbSB0byBiZSBncm93aW5nIGxlc3Mgc2FkLiAKYGBge3IsIGV2YWwgPSBULG91dC53aWR0aD0iOTklIn0KCnBhbCA8LSBjKCJ0dXJxdW9pc2U0IiwibmF2eWJsdWUiLCJwZXJ1IiwiZ29sZGVucm9kMiIsImZvcmVzdGdyZWVuIiwiZGFya2dyYXkiLAogICAgICAgICAib3JhbmdlIiwibWFnZW50YSIsImxpZ2h0c2xhdGVibHVlIiwieWVsbG93Z3JlZW4iLCJtZWRpdW1ibHVlIikKCmZpZyA8LSBwbG90X2x5KGRhdGEgPSByYW5rLAogICAgICAgICAgICAgIHggPSB+YWxidW1fbmFtZSwgeSA9IH5hdmVyYWdlLHR5cGUgPSAnc2NhdHRlcicsbW9kZSA9ICdtYXJrZXJzJywKICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHNpemUgPSAxMCksCiAgICAgICAgICAgICAgY29sb3IgPSB+YWxidW1fbmFtZSwgY29sb3JzID0gcGFsLCBzcGFuID0gSSgxKSxzdHJva2UgPSBJKCJibGFjayIpLAogICAgICAgICAgICAgIHRleHQgPSB+cGFzdGUoIkdsb29tIEluZGV4IHNjb3JlOiAiLCByb3VuZChhdmVyYWdlLDMpLCAnPGJyPlRyYWNrOicsIHRyYWNrX25hbWUpKQoKZmlnIDwtIGZpZyAlPiUgYWRkX3RyYWNlKHkgPSB+Z2xvb21faW5kZXgsbW9kZSA9ICdsaW5lcyttYXJrZXJzJyx0eXBlID0gJ3NjYXR0ZXInLG5hbWUgPSAnQWxidW0gQXZlcmFnZScsCiAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IEkoJ2JsYWNrJyksc3Ryb2tlID0gSSgiYmxhY2siKSxtYXJrZXIgPSBsaXN0KHNpemUgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gfnBhc3RlKCJBbGJ1bSBhdmVyYWdlIHZhbGVuY2U6Iiwgcm91bmQoZ2xvb21faW5kZXgsMyksICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICc8YnI+QWxidW06JyxhbGJ1bV9uYW1lKSkKCmZpZyAlPiUgbGF5b3V0KHhheGlzID0gbGlzdCh6ZXJvbGluZSA9IFQsIHRpdGxlID0iQWxidW0gTmFtZSIpLAogICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QoaG92ZXJmb3JtYXQgPSAnLjNmJywgdGl0bGUgPSAnR2xvb20gSW5kZXgnKSwKICAgICAgICAgICAgICAgc2hvd2xlZ2VuZCA9IEYpCmBgYApXaXRoIHRoaXMgZ3JhcGggd2UgY2FuIGNvbmNsdWRlIHNvbWUgdGhpbmdzLiBGaXJzdCwgdGhlIHNhZGRlc3QgUGVhcmwgSmFtIGFsYnVtIHRvIGRhdGUgaXMgKipUZW4qKiwgZm9sbG93ZWQgYnkgKipZaWVsZCoqIGFuZCAqKlZpdGFsb2d5KiouIE9uIHRoZSBvdGhlciBoYW5kLCB0aGUgaGFwcGllc3QgYWxidW1zIGJlaW5nICBCYWNrc3BhY2VyIGFuZCBMaWdodG5pbmcgQm9sdCBhbmQgdGhlIHRvcHMgc2FkIHNvbmdzIGJlaW5nICoqUGFydGluZyB3YXlzKiogYW5kICoqU3VwZXJzb25pYyoqLgoKIyMgV29yZCBjb3VudHMgYnkgYWxidW0gYW5kIHNvbmcKCldpdGggdGhlIGRhdGFzZXQgSSdtIGFibGUgdG8gZG8gc29tZSBleHRyYSBleHBsb3JhdGlvbi4gRm9yIGV4YW1wbGUsIGhvdyBtYW55IHdvcmRzIGRvZXMgZWFjaCBhbGJ1bSBoYXZlIGluIGF2ZXJhZ2U/IEhvdyBtYW55IHdvcmRzIHBlciBzZWNvbmQ/IFdoYXQncyB0aGUgd29yZGllc3Qgc29uZyBhbmQgYWxidW0/IEluIG9yZGVyIHRvIGFuc3dlciB0aGlzIHF1ZXN0aW9uIGlzIGltcG9ydGFudCB0byByZWx5IGluIGFuIGltcG9ydGFudCB3b3JkIGNvdW50IHN0YXRpc3RpYy4gRm9yIHRoaXMgbWF0dGVyIGkgd2lsbCB1c2UgdGhlIGNvdW50IG9mIGRpc3RpY3RpdmUgd29yZHMgdXNlZCBpbiB0aGUgc29uZy4gQnV0LCB3aHkgdGhpcyBzdGF0aXN0aWM/IEkgY29uc2lkZXJlZCBkaXN0aW5jdCB3b3JkcyBpbiBvcmRlciB0byBhdm9pZCByZXB0ZXRpdmVuZXNzLiBGb3IgZXhhbXBsZSwgaW4gYW5hbGl6aW5nIERlZXAsIHlvdSBjYW4gaGVhciBhYm91dCA3IHRpbWVzICoqKmNhbid0IHRvdWNoIHRoZSBib3R0b20qKiouIEluIGRvaW5nIHNvIHdlIGFyZSBub3QgYmVpbmcgZ3VpZGVkIGJ5IHRoZSByZXBldGl2ZW5lc3Mgb2YgdGhlIHNvbmcsIGJ1dCB0aGUgZGlmZmVyZW50IHdvcmRzIHVzZWQuCgpgYGAge3IsIGV2YWwgPSBUfQpwaiAlPiUgZ3JvdXBfYnkodHJhY2tfbmFtZSxhbGJ1bV9uYW1lKSAlPiUgc3VtbWFyaXNlKGRpc3RpbmN0X3dvcmRzID0gbWVhbihkaXN0aW5jdF93b3JkcyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd29yZF9wZXJfc29uZyA9IG1lYW4od29yZHMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHdvcmRzX3Blcl9zZWNvbmQgPSBtZWFuKHdvcmRzX3Blcl9zZWNvbmQpKSAlPiUgICAgICAgICAgICAgIGFycmFuZ2UoZGVzYyhkaXN0aW5jdF93b3JkcykpICU+JSBoZWFkKDEwKSAlPiUKICBrYWJsZShmb3JtYXQgPSAic2ltcGxlIiwgY29sLm5hbWVzID0gc3RyX3RvX3RpdGxlKGdzdWIoIltfXSIsICIgIiwgY29sbmFtZXMoLikpKSxhbGlnbiA9ICdsY2NjYycsY2FwdGlvbiA9ICJQZWFybCBKYW0gd29yZCBjb3VudCBieSBzb25nIixkaWdpdHMgPSAzKQpgYGAKCiMjIFdvcmRjbG91ZHMKCkF0IHRoaXMgcG9pbnQgSSdhbSBhYmxlIHRvIHVzZSB0aGUgZmlsdGVyZWQgZGF0YSBpbnRvIHdvcmRjbG91ZHMgYnkgdXNpbmcgdGhlICB3b3JkIGNvdW50cyBmb3IgZWFjaCBzb25nLiBCeSBkb2luZyBzbyB0aGUgbWFpbiBvYmplY3RpdmUgaXMgdG8gdml6dWFsaXplIHRoZSBwcmljaXBhbCB3b3JkcyB1c2VkIGluIGVhY2ggc29uZyBieSBhZGRpbmcgdGhlIHRvdGFsIHdvcmRzIHVzZWQgaW4gZWFjaCBzb25nIGFjcm9zcyBhbGJ1bXMuIEkgdXNlIHRoZSBwYWNrYWdlcyBgd29yZGNsb3VkYCBhbmQgYHRtYC4gSSB0aGVuIGZvbGxvdyB0byB1c2UgdGhlIGZ1bmN0aW9ucyBgd29yZGNsb3VkYChmcm9tIGB3b3JkY2xvdWRgKSBhbmQgYERvY3VtZW50VGVybU1hdHJpeGAsIGBDb3JwdXNgIGFuZCBgVmVjdG9yU291cmNlYCAoZnJvbSBgdG1gKS4gSSBwbG90IHRoZSB0b3AgMTAwIHdvcmRzIHNlbGVjdGVkIGJ5IHRoZSB3b3JkIGNvdW50cyBpbiBlYWNoIGFsYnVtIChhZ2dyZWF0ZWQgYnkgdGhlIHN1bSBvZiBlYWNoIHdvcmRzIGNvdW50IGJ5IHNvbmcpCgpBIHNtYWxsIGNhdmVhdCBpbiB0aGlzIHNlY3Rpb246IFdvcmQgY291bnRzIGRvIG5vdCBhY2NvdW50IGZvciBzb25nIHJlcGV0aWl2ZW5lc3MuIFNvIHdvcmQgdXNhZ2UgbWlnaHQgYmUgYmlhc2VkIGluIHRlcm1zIG9mIHJlcGV0aXRpb24gd2l0aGluIGVhY2ggc29uZyBhbmQgYWxidW0uIFNlY3Rpb24gMi42IChUZXJtIGZyZXF1ZW5jeSBJbnZlcnNlIERvY3VtZW50IEZyZXF1ZW5jeSAoVEYtSURGKSkgdGFrZXMgdGhpcyBwcm9ibGVtIGludG8gYWNjb3VudCBieSB1c2luZyBhIG1lYXN1cmUgb2YgdGhlIGltcG9ydGFuY2Ugb2YgZWFjaCB3b3JkIGluIHRlYWNoIHNvbmcuCgotLS0gClRlbiAhW10oUmVzdWx0cy9Xb3JkY2xvdWQvVGVuLmpwZWcpIAotLS0gClRlbiAhW10oUmVzdWx0cy9Xb3JkY2xvdWQvVGVuLmpwZWcpIAotLS0gClZzLiAhW10oUmVzdWx0cy9Xb3JkY2xvdWQvVnMuanBlZykgCi0tLSAKVml0YWxvZ3kgIVtdKFJlc3VsdHMvV29yZGNsb3VkL3ZpdGFsb2d5LmpwZWcpIAotLS0gCk5vIENvZGUgIVtdKFJlc3VsdHMvV29yZGNsb3VkL05vIGNvZGUuanBlZykgCi0tLSAKWWllbGQgIVtdKFJlc3VsdHMvV29yZGNsb3VkL3lpZWxkLmpwZWcpIAotLS0gCkJpbmF1cmFsICFbXShSZXN1bHRzL1dvcmRjbG91ZC9CaW5hdXJhbC5qcGVnKSAKLS0tIApSaW90IEFjdCAhW10oUmVzdWx0cy9Xb3JkY2xvdWQvUmlvdCBBY3QuanBlZykgCi0tLSAKUGVhcmwgSmFtICFbXShSZXN1bHRzL1dvcmRjbG91ZC9QZWFybCBKYW0uanBlZykgCi0tLSAKQmFja3NwYWNlciAhW10oUmVzdWx0cy9Xb3JkY2xvdWQvQmFja3NwYWNlci5qcGVnKSAKLS0tIApMaWdodGhpbmcgQm9sdCAhW10oUmVzdWx0cy9Xb3JkY2xvdWQvTGlnaHRoaW5nIGJvbHQuanBlZykgCi0tLSAKR2lnYXRvbiAhW10oUmVzdWx0cy9Xb3JkY2xvdWQvR2lnYXRvbi5qcGVnKSAKLS0tIAoKIyMgVm9jYWJ1bGFyeSBkaXZlcnNpdHkKCmBgYCB7cixvdXQud2lkdGg9Ijk5JSJ9CnBqICU+JSBncm91cF9ieShhbGJ1bV9uYW1lLHRyYWNrX25hbWUpICU+JSBzdW1tYXJpc2UobnVtYmVyX3dvcmRzID0gbWVhbihkaXN0aW5jdF93b3JkcykpICU+JSAKICBnZ3Bsb3QoZGF0YSA9IC4sIGFlcyh4ID0gZmFjdG9yKGFsYnVtX25hbWUpLHkgPSBudW1iZXJfd29yZHMpKSArZ2VvbV92aW9saW4oYWxwaGEgPSAwLjcsIGNvbG9yID0gJ2JsYWNrJywgc2l6ZSA9IDAuMikrCiAgZ2VvbV9qaXR0ZXIoYWVzKHNpemUgPSBudW1iZXJfd29yZHMpLGhlaWdodCA9IDAsIHdpZHRoID0gMC4xLCBhbHBoYSA9MC4yKSArIGdlb21fYm94cGxvdChhZXMoZmlsbCA9IGFsYnVtX25hbWUpLCBhbHBoYSA9IDAuNikgKwogIGxhYnModGl0bGUgPSAiTGV4aWNhbCBEaXZlcnNpdHkgLSBWb2NhYnVsYXJ5IiwgeCA9ICIiLCB5ID0gJ0Rpc3RpbmN0IHdvcmRzIGNvdW50JykgKyB0aGVtZV9idygpICsgCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSwgc2l6ZSA9IDEwKSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSwKICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpLAogICAgICAgIGxlZ2VuZC50aXRsZSAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoc2l6ZSA9IDAuNCkpICsgZ3VpZGVzKGZpbGwgPSBGKSArIHNjYWxlX3NpemUoJ051bWJlciBvZiB3b3JkcycpCgpgYGAKCiMjIFRlcm0gRnJlcXVlbmN5IEludmVyc2UgRG9jdW1lbnQgRnJlcXVlbmN5IChURi1JREYpCgpgYGB7cixpbmNsdWRlPUYsZXZhbD1UfQpmcmVxdWVuY3kgPC0gcGogJT4lIHVubmVzdF90b2tlbnMod29yZCwgbHlyaWNzKSAlPiUgCiAgY291bnQoYWxidW1fbmFtZSwgd29yZCwgc29ydCA9IFQpICU+JSBiaW5kX3RmX2lkZih3b3JkLCBhbGJ1bV9uYW1lLCBuKQpmcmVxdWVuY3kkYWxidW1fbmFtZSA8LSBmYWN0b3IoZnJlcXVlbmN5JGFsYnVtX25hbWUsIGxldmVscyA9IGFsYnVtX2xldmVscykKCmZyZXF1ZW5jeSA8LSBzdWJzZXQoZnJlcXVlbmN5LHdvcmQgIT0gImUiKQpmcmVxdWVuY3kgPC0gc3Vic2V0KGZyZXF1ZW5jeSx3b3JkICE9ICJnIikKZnJlcXVlbmN5IDwtIHN1YnNldChmcmVxdWVuY3ksd29yZCAhPSAicmUiKQpmcmVxdWVuY3kgPC0gc3Vic2V0KGZyZXF1ZW5jeSx3b3JkICE9ICJyIikKZnJlcXVlbmN5IDwtIHN1YnNldChmcmVxdWVuY3ksd29yZCAhPSAib2siKQpmcmVxdWVuY3kgPC0gc3Vic2V0KGZyZXF1ZW5jeSx3b3JkICE9ICJ1aG9oIikKZnJlcXVlbmN5IDwtIHN1YnNldChmcmVxdWVuY3ksd29yZCAhPSAiYmVuIikKZnJlcXVlbmN5IDwtIHN1YnNldChmcmVxdWVuY3ksd29yZCAhPSAiaGEiKQpmcmVxdWVuY3kgPC0gc3Vic2V0KGZyZXF1ZW5jeSx3b3JkICE9ICJiZW4iKQpmcmVxdWVuY3kgPC0gc3Vic2V0KGZyZXF1ZW5jeSx3b3JkICE9ICJ3YWgiKQpmcmVxdWVuY3kgPC0gc3Vic2V0KGZyZXF1ZW5jeSx3b3JkICE9ICJ3b2FoIikKZnJlcXVlbmN5IDwtIHN1YnNldChmcmVxdWVuY3ksd29yZCAhPSAiZCIpCmBgYApgYGB7cixvdXQud2lkdGg9Ijk5JSJ9CgphbGJ1bV9sZXZlbHMxIDwtIGMoIlRlbiIsICJWcy4iLCAiVml0YWxvZ3kiLCAiTm8gQ29kZSIsICJZaWVsZCIsICJCaW5hdXJhbCIpCmFsYnVtX2xldmVsczIgPC0gYygiUmlvdCBBY3QiLCAiUGVhcmwgSmFtIiwgIkJhY2tzcGFjZXIiLCAiTGlnaHRuaW5nIEJvbHQiLCAiR2lnYXRvbiIpCgpmcmVxdWVuY3lbZnJlcXVlbmN5JGFsYnVtX25hbWUgJWluJSBhbGJ1bV9sZXZlbHMxLF0gJT4lIGdyb3VwX2J5KGFsYnVtX25hbWUpICU+JSAKICBzbGljZV9tYXgodGZfaWRmLCBuID0gMTUpICU+JSB1bmdyb3VwKCkgJT4lCiAgZ2dwbG90KGFlcyh0Zl9pZGYsZmN0X3Jlb3JkZXIod29yZCwgdGZfaWRmKSwgZmlsbCA9IGFsYnVtX25hbWUpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGKSArIGZhY2V0X3dyYXAofmFsYnVtX25hbWUsIG5jb2wgPSAzLCBzY2FsZXMgPSAiZnJlZSIpICsKICBsYWJzKHggPSAidGYtaWRmIiwgeSA9IE5VTEwpK3RoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNy41KSkKCmZyZXF1ZW5jeVtmcmVxdWVuY3kkYWxidW1fbmFtZSAlaW4lIGFsYnVtX2xldmVsczIsXSAlPiUgZ3JvdXBfYnkoYWxidW1fbmFtZSkgJT4lIAogIHNsaWNlX21heCh0Zl9pZGYsIG4gPSAxNSkgJT4lIHVuZ3JvdXAoKSAlPiUKICBnZ3Bsb3QoYWVzKHRmX2lkZixmY3RfcmVvcmRlcih3b3JkLCB0Zl9pZGYpLCBmaWxsID0gYWxidW1fbmFtZSkpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEYpICsgZmFjZXRfd3JhcCh+YWxidW1fbmFtZSwgbmNvbCA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKwogIGxhYnMoeCA9ICJ0Zi1pZGYiLCB5ID0gTlVMTCkrdGhlbWUoYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA3LjUpKQoKYGBgCgojIFNlbnRpbWVudCBBbmFseXNpcyBhbmQgTmF0dXJhbCBMYW5ndWFnZSBQcm9jZXNzaW5nCiMjIE5DUiBTZW50aW1lbnQKYGBge3J9CnBqJGFsYnVtX25hbWUgPC0gZmFjdG9yKHBqJGFsYnVtX25hbWUsIGxldmVscyA9IGFsYnVtX2xldmVscykKcGpbcGokYWxidW1fbmFtZSAlaW4lIGFsYnVtX2xldmVsczEsXSAlPiUgZ3JvdXBfYnkoYWxidW1fbmFtZSkgJT4lIHN1bW1hcmlzZShmZWFyID0gc3VtKGZlYXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5nZXIgPSBzdW0oYW5nZXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW50aWNpcGF0aW9uID0gc3VtKGFudGljaXBhdGlvbiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXNndXN0ID0gc3VtKGRpc2d1c3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgam95ID0gc3VtKGpveSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG5lZ2F0aXZlID0gc3VtKG5lZ2F0aXZlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcG9zaXRpdmUgPSBzdW0ocG9zaXRpdmUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FkbmVzcyA9IHN1bShzYWRuZXNzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnByaXNlID0gc3VtKHN1cnByaXNlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydXN0ID0gc3VtKHRydXN0KSkgJT4lIAogIHJlc2hhcGUyOjptZWx0KGlkLnZhcnMgPSBjKCdhbGJ1bV9uYW1lJyksIHZhbHVlLm5hbWUgPSAnc2VudGltZW50JykgJT4lIAogIGdncGxvdChhZXMoeCA9IHZhcmlhYmxlLCB5ID0gc2VudGltZW50LCBmaWxsID0gc2VudGltZW50KSkgKyBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKyBjb29yZF9mbGlwKCkrCiAgZmFjZXRfd3JhcCh+YWxidW1fbmFtZSwgbmNvbCA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyB0aGVtZV9idygpICsgc2NhbGVfZmlsbF92aXJpZGlzKCkgKyAKICBsYWJzKHkgPSAiV29yZCBjb3VudCIsIHggPSAiTkNSIFNlbnRpbWVudCIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKQoKcGpbcGokYWxidW1fbmFtZSAlaW4lIGFsYnVtX2xldmVsczIsXSAlPiUgZ3JvdXBfYnkoYWxidW1fbmFtZSkgJT4lIHN1bW1hcmlzZShmZWFyID0gc3VtKGZlYXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5nZXIgPSBzdW0oYW5nZXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW50aWNpcGF0aW9uID0gc3VtKGFudGljaXBhdGlvbiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXNndXN0ID0gc3VtKGRpc2d1c3QpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgam95ID0gc3VtKGpveSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIG5lZ2F0aXZlID0gc3VtKG5lZ2F0aXZlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcG9zaXRpdmUgPSBzdW0ocG9zaXRpdmUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FkbmVzcyA9IHN1bShzYWRuZXNzKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnByaXNlID0gc3VtKHN1cnByaXNlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRydXN0ID0gc3VtKHRydXN0KSkgJT4lIAogIHJlc2hhcGUyOjptZWx0KGlkLnZhcnMgPSBjKCdhbGJ1bV9uYW1lJyksIHZhbHVlLm5hbWUgPSAnc2VudGltZW50JykgJT4lIAogIGdncGxvdChhZXMoeCA9IHZhcmlhYmxlLCB5ID0gc2VudGltZW50LCBmaWxsID0gc2VudGltZW50KSkgKyBnZW9tX2JhcihzdGF0ID0gJ2lkZW50aXR5JykgKyBjb29yZF9mbGlwKCkrCiAgZmFjZXRfd3JhcCh+YWxidW1fbmFtZSwgbmNvbCA9IDMsIHNjYWxlcyA9ICJmcmVlIikgKyB0aGVtZV9idygpICsgc2NhbGVfZmlsbF92aXJpZGlzKCkgKyAKICBsYWJzKHkgPSAiV29yZCBjb3VudCIsIHggPSAiTkNSIFNlbnRpbWVudCIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKQoKYGBgCgojIyBSYWRhcnBsb3RzCgpgYGB7cn0KcmFkYXIgPC0gcGpbLGMoZ3JlcCgnYWxidW1fbmFtZScsIGNvbG5hbWVzKHBqKSksCiAgICAgICAgICAgICAgICBncmVwKCdmZWFyJywgY29sbmFtZXMocGopKSwKICAgICAgICAgICAgICAgIGdyZXAoJ2FuZ2VyJywgY29sbmFtZXMocGopKSwKICAgICAgICAgICAgICAgIGdyZXAoJ2FudGljaXBhdGlvbicsIGNvbG5hbWVzKHBqKSksCiAgICAgICAgICAgICAgICBncmVwKCdkaXNndXN0JywgY29sbmFtZXMocGopKSwKICAgICAgICAgICAgICAgIGdyZXAoJ3NhZG5lc3MnLCBjb2xuYW1lcyhwaikpKV0gJT4lIAogIGdyb3VwX2J5KGFsYnVtX25hbWUpICU+JSBzdW1tYXJpc2UoZmVhciA9IG1lYW4oZmVhciksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbmdlciA9IG1lYW4oYW5nZXIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW50aWNpcGF0aW9uID0gbWVhbihhbnRpY2lwYXRpb24pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzZ3VzdCA9IG1lYW4oZGlzZ3VzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzYWRuZXNzID0gbWVhbihzYWRuZXNzKSkKY29sbmFtZXMocmFkYXIpIDwtIHN0cl90b190aXRsZShjb2xuYW1lcyhyYWRhcikpCmNoYXJ0SlNSYWRhcihyYWRhcikKCnJhZGFyIDwtIHBqWyxjKGdyZXAoJ2FsYnVtX25hbWUnLCBjb2xuYW1lcyhwaikpLAogICAgICAgICAgICAgICAgZ3JlcCgnam95JywgY29sbmFtZXMocGopKSwKICAgICAgICAgICAgICAgIGdyZXAoJ3N1cnByaXNlJywgY29sbmFtZXMocGopKSwKICAgICAgICAgICAgICAgIGdyZXAoJ3RydXN0JywgY29sbmFtZXMocGopKSldICU+JSAKICBncm91cF9ieShhbGJ1bV9uYW1lKSAlPiUgc3VtbWFyaXNlKGpveSA9IG1lYW4oam95KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1cnByaXNlID0gbWVhbihzdXJwcmlzZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnVzdCA9IG1lYW4odHJ1c3QpCiAgKQpjb2xuYW1lcyhyYWRhcikgPC0gc3RyX3RvX3RpdGxlKGNvbG5hbWVzKHJhZGFyKSkKY2hhcnRKU1JhZGFyKHJhZGFyKQoKcmFkYXIgPC0gcGpbLGMoZ3JlcCgnYWxidW1fbmFtZScsIGNvbG5hbWVzKHBqKSksCiAgICAgICAgICAgICAgIGdyZXAoJ3Bvc2l0aXZlJywgY29sbmFtZXMocGopKSwKICAgICAgICAgICAgICAgZ3JlcCgnbmVnYXRpdmUnLCBjb2xuYW1lcyhwaikpKV0gJT4lIAogIGdyb3VwX2J5KGFsYnVtX25hbWUpICU+JSBzdW1tYXJpc2UocG9zaXRpdmUgPSBtZWFuKHBvc2l0aXZlKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5lZ2F0aXZlID0gbWVhbihuZWdhdGl2ZSkKICApCgpjb2xuYW1lcyhyYWRhcikgPC0gc3RyX3RvX3RpdGxlKGNvbG5hbWVzKHJhZGFyKSkKY2hhcnRKU1JhZGFyKHJhZGFyKQpgYGAKCiMjIEJpLWdyYW1zIGFuZCB0cmktZ3JhbXMKCmBgYHtyfQpiaWdyYW1zIDwtICBwalssYyhncmVwKCd0cmFja19uYW1lJywgY29sbmFtZXMocGopKSwKICAgICAgICAgICAgICAgICAgIGdyZXAoJ2FsYnVtX25hbWUnLCBjb2xuYW1lcyhwaikpLAogICAgICAgICAgICAgICAgICAgZ3JlcCgnbHlyaWNzJywgY29sbmFtZXMocGopKSldICU+JQogICAgICAgICAgICB1bm5lc3RfdG9rZW5zKGJpZ3JhbSwgbHlyaWNzLCB0b2tlbiA9ICJuZ3JhbXMiLCBuID0gMikgJT4lIAogICAgICAgICAgICBzZXBhcmF0ZShiaWdyYW0sIGMoIndvcmQxIiwgIndvcmQyIiksIHNlcCA9ICIgIikgJT4lIAogICAgICAgICAgICBmaWx0ZXIoIXdvcmQxICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUKICAgICAgICAgICAgZmlsdGVyKCF3b3JkMiAlaW4lIHN0b3Bfd29yZHMkd29yZCkgJT4lIAogICAgICAgICAgICBmaWx0ZXIod29yZDEgIT0gd29yZDIpICU+JSAKICAgICAgICAgICAgdW5pdGUoYmlncmFtLCB3b3JkMSwgd29yZDIsIHNlcCA9ICIgIikgJT4lIAogICAgICAgICAgICBpbm5lcl9qb2luKHBqKSAgJT4lIAogICAgICAgICAgICBjb3VudChiaWdyYW0sIGFsYnVtX25hbWUsIHNvcnQgPSBUUlVFKSAlPiUgICAKICAgICAgICAgICAgZ3JvdXBfYnkoYWxidW1fbmFtZSkgJT4lIAogICAgICAgICAgICBzbGljZShzZXFfbGVuKDEwKSkgJT4lIAogICAgICAgICAgICB1bmdyb3VwKCkgJT4lIGFycmFuZ2UoYWxidW1fbmFtZSxuKQoKYmlncmFtcyRhbGJ1bV9uYW1lIDwtIGZhY3RvcihiaWdyYW1zJGFsYnVtX25hbWUsIGxldmVscyA9IGFsYnVtX2xldmVscykKCmdncGxvdChkYXRhID0gYmlncmFtc1tiaWdyYW1zJGFsYnVtX25hbWUgJWluJSBhbGJ1bV9sZXZlbHMxLF0sIGFlcyh4ID0gYmlncmFtLCB5ID0gbiwgZmlsbCA9IGFsYnVtX25hbWUpKSsKZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGKSArIGNvb3JkX2ZsaXAoKSArIHRoZW1lX2J3KCkrIGxhYnMoeSA9ICdOdW1iZXIgb2YgcmVwZXRpdGlvbnMnLCB4ID0nQmlncmFtJykrCmZhY2V0X3dyYXAofmFsYnVtX25hbWUsIHNjYWxlcyA9ICJmcmVlX3kiKSAKZ2dwbG90KGRhdGEgPSBiaWdyYW1zW2JpZ3JhbXMkYWxidW1fbmFtZSAlaW4lIGFsYnVtX2xldmVsczIsXSwgYWVzKHggPSBiaWdyYW0sIHkgPSBuLCBmaWxsID0gYWxidW1fbmFtZSkpKwpnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEYpICsgY29vcmRfZmxpcCgpICsgdGhlbWVfYncoKSsgbGFicyh5ID0gJ051bWJlciBvZiByZXBldGl0aW9ucycsIHggPSdCaWdyYW0nKSsKZmFjZXRfd3JhcCh+YWxidW1fbmFtZSwgc2NhbGVzID0gImZyZWVfeSIpIApgYGAK